mirror of
https://github.com/janet-lang/janet
synced 2026-04-02 13:01:28 +00:00
Compare commits
1 Commits
nanbox_poi
...
undo-deep-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5adfb75a25 |
@@ -10,9 +10,3 @@ tasks:
|
||||
gmake test
|
||||
sudo gmake install
|
||||
sudo gmake uninstall
|
||||
- build-sanitizers: |
|
||||
cd janet
|
||||
CFLAGS="-g -O2 -fsanitize=address,undefined" gmake
|
||||
gmake test
|
||||
sudo gmake install
|
||||
sudo gmake uninstall
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
image: openbsd/7.7
|
||||
image: openbsd/7.4
|
||||
sources:
|
||||
- https://git.sr.ht/~bakpakin/janet
|
||||
packages:
|
||||
@@ -10,17 +10,13 @@ tasks:
|
||||
gmake
|
||||
gmake test
|
||||
doas gmake install
|
||||
gmake test-install
|
||||
doas gmake uninstall
|
||||
- meson_min: |
|
||||
cd janet
|
||||
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
|
||||
cd build_meson_min
|
||||
ninja
|
||||
- meson_reduced: |
|
||||
cd janet
|
||||
meson setup build_meson_reduced --buildtype=release -Dreduced_os=true
|
||||
cd build_meson_reduced
|
||||
ninja
|
||||
- meson_prf: |
|
||||
cd janet
|
||||
meson setup build_meson_prf --buildtype=release -Dprf=true
|
||||
|
||||
8
.github/cosmo/setup
vendored
8
.github/cosmo/setup
vendored
@@ -1,19 +1,19 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt update
|
||||
sudo apt-get install -y ca-certificates libssl-dev\
|
||||
qemu-utils qemu-user-static\
|
||||
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/4.0.2/cosmocc-4.0.2.zip
|
||||
wget https://github.com/jart/cosmopolitan/releases/download/3.3.3/cosmocc-3.3.3.zip
|
||||
mkdir -p cosmocc
|
||||
cd cosmocc
|
||||
unzip ../cosmocc-4.0.2.zip
|
||||
unzip ../cosmocc-3.3.3.zip
|
||||
|
||||
# register
|
||||
cd /sc/cosmocc
|
||||
|
||||
7
.github/workflows/codeql.yml
vendored
7
.github/workflows/codeql.yml
vendored
@@ -27,16 +27,15 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-and-quality
|
||||
tools: linked
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest ]
|
||||
os: [ ubuntu-latest, macos-13 ]
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ macos-latest, macos-15-intel ]
|
||||
os: [ macos-latest ]
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
|
||||
68
.github/workflows/test.yml
vendored
68
.github/workflows/test.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, ubuntu-24.04-arm, macos-latest, macos-14, macos-15-intel ]
|
||||
os: [ ubuntu-latest, macos-latest, macos-13 ]
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
@@ -21,25 +21,11 @@ jobs:
|
||||
- name: Test the project
|
||||
run: make test
|
||||
|
||||
test-posix-sanitizers:
|
||||
name: Build and test on POSIX systems with sanitizers turned on
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest ]
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: Compile the project
|
||||
run: make clean && CFLAGS="-g -O2 -fsanitize=address,undefined" make
|
||||
- name: Test the project
|
||||
run: make test
|
||||
|
||||
test-windows:
|
||||
name: Build and test on Windows
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-latest, windows-2022 ]
|
||||
os: [ windows-latest, windows-2019 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
@@ -56,32 +42,11 @@ jobs:
|
||||
shell: cmd
|
||||
run: build_win dist
|
||||
|
||||
test-windows-sanitizers:
|
||||
name: Build and test on Windows with sanitizers
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-latest ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: Setup MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Build the project
|
||||
shell: cmd
|
||||
run: set SANITIZE=1 & build_win
|
||||
- name: Test the project
|
||||
shell: cmd
|
||||
run: set VERBOSE=1 & build_win test
|
||||
- name: Test installer build
|
||||
shell: cmd
|
||||
run: build_win dist
|
||||
|
||||
test-windows-min:
|
||||
name: Build and test on Windows Minimal build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-2022 ]
|
||||
os: [ windows-2019 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
@@ -167,26 +132,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: Enable qemu
|
||||
run: docker run --privileged --rm tonistiigi/binfmt --install s390x
|
||||
- name: Build and run on emulated architecture
|
||||
run: docker run --rm -v .:/janet --platform linux/s390x alpine sh -c "apk update && apk add --no-interactive git build-base && cd /janet && make -j3 && make test"
|
||||
|
||||
test-cosmo:
|
||||
name: Test build 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: Do Qemu build and test
|
||||
run: |
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
docker run --rm -v .:/janet --platform linux/s390x ubuntu bash -c "apt-get -y update && apt-get -y install git build-essential && cd /janet && make -j3 && make test"
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,7 +12,6 @@ janet
|
||||
/src/include/generated/*.h
|
||||
janet-*.tar.gz
|
||||
dist
|
||||
/tmp
|
||||
|
||||
# jpm lockfile
|
||||
lockfile.janet
|
||||
|
||||
79
CHANGELOG.md
79
CHANGELOG.md
@@ -1,83 +1,12 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## Unreleased - ???
|
||||
- Documentation fixes
|
||||
- ev/thread-chan deadlock bug fixed
|
||||
- Re-add removed support for non-blocking net/connect on windows.
|
||||
|
||||
## 1.41.2 - 2026-02-18
|
||||
- Fix regressions in `put` for arrays and buffers.
|
||||
- Add `module/add-file-extension`
|
||||
- Add `module/add-syspath`
|
||||
- Fix issue with possible stack corrpution with abstract types that modify the current fiber.
|
||||
- Allow use of the interpreter and garbage collection inside module entry for native modules.
|
||||
|
||||
## 1.41.1 - 2026-02-15
|
||||
- Revert to blocking behaior of `net/connect` on windows to fix spurious errors.
|
||||
- Allow overriding the loader when doing imports with the `:loader` argument.
|
||||
- Allow importing modules with a path extension to do what one would expect.
|
||||
- Add `find-all` argument to `module/find`
|
||||
- Add :threads, :unmarshal, :compiler, and :asm sandbox flags.
|
||||
- Add support for persistent REPL history with the environment variable `JANET_HISTFILE`
|
||||
- Fix a number of fuzzer-found compiler bugs
|
||||
- Fix windows processes launching bug with empty environment table that caused process-launch failures.
|
||||
- Add `:I`, `:V`, and `:N` flags to `os/open` for more control when creating streams.
|
||||
- Add `ev/go-gather` for a dynamic `ev/gather`.
|
||||
- Use color in script output if color is being used in REPL output.
|
||||
- Fix `varfn` macros handling of extra metadata.
|
||||
- Disallow certain degenerate uses of fibers with the ev/ module.
|
||||
- Add linting for unused bindings.
|
||||
- Add linting for extra or wrong parameters to &named functions.
|
||||
- Add `janet_optuinteger` and `janet_optuinteger64` to the C API.
|
||||
- Add `cms` combinator to PEGs.
|
||||
- Add `thaw-keep-keys` as a variant of thaw
|
||||
- The `repl` function now respects the `*repl-prompt*` dynamic binding by default.
|
||||
- Allow matching exact lengths of datastructures with the `match` macro using a dollar suffix.
|
||||
- Add initial support for Plan 9. Some modules (e.g. ev) are not yet enabled.
|
||||
- Allow specifying `:flycheck` for individual defines to annotate that they are safe to evaluate for flychecking.
|
||||
|
||||
## 1.40.1 - 2025-11-16
|
||||
- Fix `JANET_REDUCED_OS` build regression caused by `os/posix-chroot`.
|
||||
- Code formatting
|
||||
|
||||
## 1.40.0 - 2025-11-15
|
||||
- Add `os/posix-chroot`
|
||||
- Fix `ev/deadline` with interrupt race condition bug on Windows.
|
||||
- Improve `flycheck` by allowing functions and macros to define their own flycheck behavior via the metadata `:flycheck`.
|
||||
- Add `*flychecking*` dynamic binding to check if inside flycheck evalutation
|
||||
- Add `gcperthread` callback for abstract types. This lets threaded abstracts have a finalizer that is called per thread, as well as a global finalizer.
|
||||
- Add `JANET_DO_ERROR_*` flags to describe the return value of `janet_dobytes` and `janet_dostring`.
|
||||
|
||||
## 1.39.1 - 2025-08-30
|
||||
- Add support for chdir in os/spawn on older macOS versions
|
||||
- Expose channels properly in C API
|
||||
|
||||
## 1.39.0 - 2025-08-24
|
||||
- Various bug fixes
|
||||
- Add `net/socket`
|
||||
- Add support for illumos OS
|
||||
- Raise helpful errors for incorrect arguments to `import`.
|
||||
- Allow configuring `JANET_THREAD_LOCAL` during builds to allow multi-threading on unknown compilers.
|
||||
- Make `ffi/write` append to a buffer instead of insert at 0 by default.
|
||||
- Add `os/getpid` to get the current process id.
|
||||
- Add `:out` option to `os/spawn` to be able to redirect stderr to stdout with pipes.
|
||||
Add `interrupt?` argument to `ev/deadline` to use VM interruptions.
|
||||
|
||||
## 1.38.0 - 2025-03-18
|
||||
- Add `bundle/replace`
|
||||
- Add CLI flags for the `bundle/` module to install and manage bundles.
|
||||
- Improve `?` peg special termination behavior
|
||||
- Add IEEE hex floats to grammar.
|
||||
- Add buffer peg literal support
|
||||
- Improve `split` peg special edge case behavior
|
||||
- Add Arm64 .msi support
|
||||
- Add `no-reuse` argument to `net/listen` to disable reusing server sockets
|
||||
- Add `struct/rawget`
|
||||
- Fix `deep=` and `deep-not=` to better handle degenerate cases with mutable table keys
|
||||
## ??? - Unreleased
|
||||
- Add `struct/rawget` to get values from a struct without a prototype.
|
||||
- Fix `deep=` and `deep-not=` to better handle degenerate cases with mutable table keys. Keys are now compared by value rather than
|
||||
structure to avoid degenerate cases.
|
||||
- Long strings will now dedent on `\r\n` instead of just `\n`.
|
||||
- Add `ev/to-file` for synchronous resource operations
|
||||
- Improve `file/open` error message by including path
|
||||
|
||||
## 1.37.1 - 2024-12-05
|
||||
- Fix meson cross compilation
|
||||
|
||||
@@ -37,12 +37,6 @@ may require changes before being merged.
|
||||
do this indentation, or approximate as close as possible. There is a janet formatter
|
||||
in [spork](https://github.com/janet-lang/spork.git) that can be used to format code as well.
|
||||
|
||||
Bot pull requests will not be accepted, and anonymous submissions, including
|
||||
new accounts, unknown emails, and first time contributors will be subjected
|
||||
to greater scrutiny and code reivew. Automatically generated and filed bug
|
||||
reports MAY be ok, if they are of consistent and good quality, such as
|
||||
OSSFuzz or well constructed CI pipelines.
|
||||
|
||||
## C style
|
||||
|
||||
For changes to the VM and Core code, you will probably need to know C. Janet is programmed with
|
||||
@@ -67,7 +61,7 @@ ensure a consistent code style for C.
|
||||
|
||||
## Janet style
|
||||
|
||||
All janet code in the project should be formatted similar to the code in src/boot/boot.janet.
|
||||
All janet code in the project should be formatted similar to the code in core.janet.
|
||||
The auto formatting from janet.vim will work well.
|
||||
|
||||
## Typo Fixing and One-Line changes
|
||||
@@ -96,18 +90,3 @@ timely manner. In short, if you want extra functionality now, then build it.
|
||||
|
||||
* Include a good description of the problem that is being solved
|
||||
* Include descriptions of potential solutions if you have some in mind.
|
||||
|
||||
## LLMs, Tool Usage, and Transparency
|
||||
|
||||
All usage of Large Language Models (LLMs), Neural Networks, "AI" tools, and
|
||||
other tools such as software fuzzers or static analyzers must be disclosed.
|
||||
This applies to pull requests, email patches, bug reports, and any other
|
||||
meaningful contribution to Janet's source code. Please also refrain from using
|
||||
generative AI for code that will be embedded in the Janet runtime, which include
|
||||
all C source files as well as boot.janet. All code should be well
|
||||
and completely understood by the human author, including test cases. Large and
|
||||
obviously AI-driven changes will be rejected. Be mindful and transparent on the
|
||||
copyright implications of any submitted code. We will use discretion when
|
||||
accepting generated test cases for bug reproductions, one-line bug
|
||||
fixes, or typo fixes. Often, these can be trivially rewritten by a human to
|
||||
avoid the problem.
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2026 Calvin Rose and contributors
|
||||
Copyright (c) 2023 Calvin Rose and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
68
Makefile
68
Makefile
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2026 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
|
||||
@@ -47,7 +47,6 @@ SPORK_TAG?=master
|
||||
HAS_SHARED?=1
|
||||
DEBUGGER=gdb
|
||||
SONAME_SETTER=-Wl,-soname,
|
||||
STRIPFLAGS=-x -S
|
||||
|
||||
# For cross compilation
|
||||
HOSTCC?=$(CC)
|
||||
@@ -55,7 +54,7 @@ HOSTAR?=$(AR)
|
||||
# Symbols are (optionally) removed later, keep -g as default!
|
||||
CFLAGS?=-O2 -g
|
||||
LDFLAGS?=-rdynamic
|
||||
LIBJANET_LDFLAGS?=$(LDFLAGS)
|
||||
LIBJANET_LDFLAGS?=$(LD_FLAGS)
|
||||
RUN:=$(RUN)
|
||||
|
||||
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC
|
||||
@@ -80,12 +79,6 @@ ifeq ($(UNAME), Darwin)
|
||||
LDCONFIG:=true
|
||||
else ifeq ($(UNAME), Linux)
|
||||
CLIBS:=$(CLIBS) -lrt -ldl
|
||||
else ifeq ($(UNAME), SunOS)
|
||||
BUILD_CFLAGS+=-D__EXTENSIONS__ -DJANET_NO_NANBOX
|
||||
BOOT_CFLAGS+=-D__EXTENSIONS__ -DJANET_NO_NANBOX
|
||||
CLIBS:=-lsocket -lm
|
||||
STRIPFLAGS=-x
|
||||
LDCONFIG:=false
|
||||
endif
|
||||
|
||||
# For other unix likes, add flags here!
|
||||
@@ -101,18 +94,12 @@ endif
|
||||
endif
|
||||
|
||||
# Mingw
|
||||
MINGW_COMPILER=
|
||||
ifeq ($(findstring MINGW,$(UNAME)), MINGW)
|
||||
MINGW_COMPILER=gcc
|
||||
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
|
||||
COMPILER_VERSION:=$(shell $(CC) --version)
|
||||
ifeq ($(findstring clang,$(COMPILER_VERSION)), clang)
|
||||
MINGW_COMPILER=clang
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
@@ -219,14 +206,9 @@ build/%.bin.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
|
||||
########################
|
||||
|
||||
ifeq ($(UNAME), Darwin)
|
||||
SONAME=libjanet.1.41.dylib
|
||||
SONAME=libjanet.1.37.dylib
|
||||
else
|
||||
SONAME=libjanet.so.1.41
|
||||
endif
|
||||
|
||||
ifeq ($(MINGW_COMPILER), clang)
|
||||
SONAME=
|
||||
SONAME_SETTER=
|
||||
SONAME=libjanet.so.1.37
|
||||
endif
|
||||
|
||||
build/c/shell.c: src/mainclient/shell.c
|
||||
@@ -260,7 +242,6 @@ $(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS)
|
||||
# Testing assumes HOSTCC=CC
|
||||
|
||||
TEST_SCRIPTS=$(wildcard test/suite*.janet)
|
||||
EXAMPLE_SCRIPTS=$(wildcard examples/*.janet)
|
||||
|
||||
repl: $(JANET_TARGET)
|
||||
$(RUN) ./$(JANET_TARGET)
|
||||
@@ -268,26 +249,21 @@ repl: $(JANET_TARGET)
|
||||
debug: $(JANET_TARGET)
|
||||
$(DEBUGGER) ./$(JANET_TARGET)
|
||||
|
||||
VALGRIND_COMMAND=$(RUN) valgrind --leak-check=full --quiet
|
||||
CALLGRIND_COMMAND=$(RUN) valgrind --tool=callgrind
|
||||
VALGRIND_COMMAND=valgrind --leak-check=full --quiet
|
||||
|
||||
valgrind: $(JANET_TARGET)
|
||||
$(VALGRIND_COMMAND) ./$(JANET_TARGET)
|
||||
|
||||
test: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
|
||||
test: $(JANET_TARGET) $(TEST_PROGRAMS)
|
||||
for f in test/suite*.janet; do $(RUN) ./$(JANET_TARGET) "$$f" || exit; done
|
||||
for f in examples/*.janet; do $(RUN) ./$(JANET_TARGET) -k "$$f"; done
|
||||
|
||||
valtest: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
|
||||
valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
|
||||
for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
|
||||
for f in examples/*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) -k "$$f"; done
|
||||
for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done
|
||||
|
||||
callgrind: $(JANET_TARGET)
|
||||
$(CALLGRIND_COMMAND) ./$(JANET_TARGET)
|
||||
|
||||
calltest: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
|
||||
for f in test/suite*.janet; do $(CALLGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
|
||||
for f in examples/*.janet; do $(CALLGRIND_COMMAND) ./$(JANET_TARGET) -k "$$f"; done
|
||||
for f in test/suite*.janet; do valgrind --tool=callgrind ./$(JANET_TARGET) "$$f" || exit; done
|
||||
|
||||
########################
|
||||
##### Distribution #####
|
||||
@@ -301,7 +277,7 @@ build/janet-%.tar.gz: $(JANET_TARGET) \
|
||||
README.md build/c/janet.c build/c/shell.c
|
||||
mkdir -p build/$(JANET_DIST_DIR)/bin
|
||||
cp $(JANET_TARGET) build/$(JANET_DIST_DIR)/bin/
|
||||
strip $(STRIPFLAGS) 'build/$(JANET_DIST_DIR)/bin/janet'
|
||||
strip -x -S 'build/$(JANET_DIST_DIR)/bin/janet'
|
||||
mkdir -p build/$(JANET_DIST_DIR)/include
|
||||
cp build/janet.h build/$(JANET_DIST_DIR)/include/
|
||||
mkdir -p build/$(JANET_DIST_DIR)/lib/
|
||||
@@ -346,23 +322,22 @@ build/janet.pc: $(JANET_TARGET)
|
||||
echo 'Libs.private: $(CLIBS)' >> $@
|
||||
|
||||
install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc build/janet.h
|
||||
$(eval JANET_VERSION := $(shell $(JANET_TARGET) -e '(print janet/version)'))
|
||||
mkdir -p '$(DESTDIR)$(BINDIR)'
|
||||
cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet'
|
||||
strip $(STRIPFLAGS) '$(DESTDIR)$(BINDIR)/janet'
|
||||
strip -x -S '$(DESTDIR)$(BINDIR)/janet'
|
||||
mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
|
||||
cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
|
||||
ln -sf ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h'
|
||||
mkdir -p '$(DESTDIR)$(JANET_PATH)'
|
||||
mkdir -p '$(DESTDIR)$(LIBDIR)'
|
||||
if test $(UNAME) = Darwin ; then \
|
||||
cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.$(JANET_VERSION).dylib' ; \
|
||||
cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.$(shell $(JANET_TARGET) -e '(print janet/version)').dylib' ; \
|
||||
ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.dylib' ; \
|
||||
ln -sf libjanet.$(JANET_VERSION).dylib $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
|
||||
ln -sf libjanet.$(shell $(JANET_TARGET) -e '(print janet/version)').dylib $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
|
||||
else \
|
||||
cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.so.$(JANET_VERSION)' ; \
|
||||
cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)')' ; \
|
||||
ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.so' ; \
|
||||
ln -sf libjanet.so.$(JANET_VERSION) $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
|
||||
ln -sf libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)') $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
|
||||
fi
|
||||
cp $(JANET_STATIC_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.a'
|
||||
mkdir -p '$(DESTDIR)$(JANET_MANPATH)'
|
||||
@@ -419,6 +394,9 @@ clean:
|
||||
-rm -rf build vgcore.* callgrind.*
|
||||
-rm -rf test/install/build test/install/modpath
|
||||
|
||||
test-install:
|
||||
echo "JPM has been removed from default install."
|
||||
|
||||
help:
|
||||
@echo
|
||||
@echo 'Janet: A Dynamic Language & Bytecode VM'
|
||||
@@ -430,8 +408,7 @@ help:
|
||||
@echo ' make test Test a built Janet'
|
||||
@echo ' make valgrind Assess Janet with Valgrind'
|
||||
@echo ' make callgrind Assess Janet with Valgrind, using Callgrind'
|
||||
@echo ' make valtest Run the test suite and examples with Valgrind to check for memory leaks'
|
||||
@echo ' make calltest Run the test suite and examples with Callgrind'
|
||||
@echo ' make valtest Run the test suite with Valgrind to check for memory leaks'
|
||||
@echo ' make dist Create a distribution tarball'
|
||||
@echo ' make docs Generate documentation'
|
||||
@echo ' make debug Run janet with GDB or LLDB'
|
||||
@@ -441,9 +418,6 @@ help:
|
||||
@echo " make format Format Janet's own source files"
|
||||
@echo ' make grammar Generate a TextMate language grammar'
|
||||
@echo
|
||||
@echo ' make install-jpm-git Install jpm into the current filesystem'
|
||||
@echo ' make install-spork-git Install spork into the current filesystem'
|
||||
@echo
|
||||
|
||||
.PHONY: clean install install-jpm-git install-spork-git repl debug valgrind test \
|
||||
valtest callgrind callgrind-test dist uninstall docs grammar format help compile-commands
|
||||
.PHONY: clean install repl debug valgrind test \
|
||||
valtest dist uninstall docs grammar format help compile-commands
|
||||
|
||||
73
README.md
73
README.md
@@ -148,40 +148,8 @@ You can get the source on [GitHub](https://github.com/janet-lang/janet) or
|
||||
[SourceHut](https://git.sr.ht/~bakpakin/janet). While the GitHub repo is the official repo,
|
||||
the SourceHut mirror is actively maintained.
|
||||
|
||||
## Spork and JPM
|
||||
|
||||
Spork and JPM are two companion projects to Janet. They are optional, especially in an embedding use case.
|
||||
|
||||
Spork is a collection of common utility modules, and several packaged scripts
|
||||
like `janet-format` for code formatting, `janet-netrepl` for a socket-based
|
||||
REPL, and `janet-pm` for a comprehensive Janet project manager tool. The
|
||||
modules in `spork` are less stable than the interfaces in core Janet, although
|
||||
we try to prevent breaking changes to existing modules, with a preference to
|
||||
add new modules and functions. Spork requires a C compiler to build and install
|
||||
various extenstion components such as miniz and JSON utilities. Many spork
|
||||
sub-modules, for example spork/path, are independent and can be manually
|
||||
vendored in programmer projects without fully installing spork.
|
||||
|
||||
When install Spork, scripts will be installed to $JANET_PATH/bin/ on POSIX systems by default.
|
||||
This likely needs to be added to the path to use these scripts.
|
||||
|
||||
JPM is the older, more opinionated, project manager tool, which has it's pros
|
||||
and cons. It does not require a C compiler to build and install, but is less
|
||||
flexible and is not receiving many changes and improvements going forward. It
|
||||
may also be harder to configure correctly on new systems. In that sense, it may
|
||||
be more stable.
|
||||
|
||||
JPM will install to /usr/local/bin/ on posix systems by default, which may or
|
||||
may not be on your PATH.
|
||||
|
||||
## Building
|
||||
|
||||
When building from source, for stability, please use the latest tagged release. For
|
||||
example, run `git checkout $(git describe --tags --abbrev=0)` after cloning but
|
||||
before building. For the latest development, build directly on the master
|
||||
branch. The master branch is not-necessarily stable as most Janet development
|
||||
happens directly on the master branch.
|
||||
|
||||
### macOS and Unix-like
|
||||
|
||||
The Makefile is non-portable and requires GNU-flavored make.
|
||||
@@ -192,34 +160,11 @@ make
|
||||
make test
|
||||
make repl
|
||||
make install
|
||||
make install-spork-git # optional
|
||||
make install-jpm-git # optional
|
||||
make install-jpm-git
|
||||
```
|
||||
|
||||
Find out more about the available make targets by running `make help`.
|
||||
|
||||
### Alpine Linux
|
||||
|
||||
To build a statically-linked build of Janet, Alpine Linux + MUSL is a good
|
||||
combination. Janet can also be built inside a docker container or similar in
|
||||
this manner. This is a great way to try Janet without committing to a full
|
||||
install or needing to customize the default install.
|
||||
|
||||
```sh
|
||||
docker run -it --rm alpine /bin/ash
|
||||
$ apk add make gcc musl-dev git
|
||||
$ git clone https://github.com/janet-lang/janet.git
|
||||
$ cd janet
|
||||
$ make -j10
|
||||
$ make test
|
||||
$ make install
|
||||
$ make install-spork-git # optional
|
||||
$ make install-jpm-git # optional
|
||||
```
|
||||
|
||||
Note that for a true statically-linked binary with MUSL, one needs to add `-static` to the Makefile flags. This
|
||||
will also disable runtime loading of native modules (plugins) as well as the FFI.
|
||||
|
||||
### 32-bit Haiku
|
||||
|
||||
32-bit Haiku build instructions are the same as the UNIX-like build instructions,
|
||||
@@ -231,8 +176,7 @@ make CC=gcc-x86
|
||||
make test
|
||||
make repl
|
||||
make install
|
||||
make install-spork-git # optional
|
||||
make install-jpm-git # optional
|
||||
make install-jpm-git
|
||||
```
|
||||
|
||||
### FreeBSD
|
||||
@@ -246,8 +190,7 @@ gmake
|
||||
gmake test
|
||||
gmake repl
|
||||
gmake install
|
||||
gmake install-spork-git # optional
|
||||
gmake install-jpm-git # optional
|
||||
gmake install-jpm-git
|
||||
```
|
||||
|
||||
### NetBSD
|
||||
@@ -255,10 +198,6 @@ gmake install-jpm-git # optional
|
||||
NetBSD build instructions are the same as the FreeBSD build instructions.
|
||||
Alternatively, install the package directly with `pkgin install janet`.
|
||||
|
||||
### illumos
|
||||
|
||||
Building on illumos is exactly the same as building on FreeBSD.
|
||||
|
||||
### Windows
|
||||
|
||||
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15#) or [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15#).
|
||||
@@ -268,7 +207,7 @@ Building on illumos is exactly the same as building on FreeBSD.
|
||||
|
||||
To build an `.msi` installer executable, in addition to the above steps, you will have to:
|
||||
|
||||
5. Install, or otherwise add to your PATH the [WiX 3.14 Toolset](https://github.com/wixtoolset/wix3/releases).
|
||||
5. Install, or otherwise add to your PATH the [WiX 3.11 Toolset](https://github.com/wixtoolset/wix3/releases).
|
||||
6. Run `build_win dist`.
|
||||
|
||||
Now you should have an `.msi`. You can run `build_win install` to install the `.msi`, or execute the file itself.
|
||||
@@ -362,8 +301,8 @@ If installed, you can also run `man janet` to get usage information.
|
||||
## Embedding
|
||||
|
||||
Janet can be embedded in a host program very easily. The normal build
|
||||
will create a file `build/c/janet.c`, a C source code file that
|
||||
that contains the amalgamated source to Janet. This file, along with
|
||||
will create a file `build/janet.c`, which is a single C file
|
||||
that contains all the source to Janet. This file, along with
|
||||
`src/include/janet.h` and `src/conf/janetconf.h`, can be dragged into any C
|
||||
project and compiled into it. Janet should be compiled with `-std=c99`
|
||||
on most compilers, and will need to be linked to the math library, `-lm`, and
|
||||
|
||||
@@ -23,17 +23,7 @@
|
||||
@rem set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD /fsanitize=address /Zi
|
||||
@rem set JANET_LINK=link /nologo clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
|
||||
|
||||
if DEFINED CLANG (
|
||||
@set COMPILER=clang-cl.exe
|
||||
) else (
|
||||
@set COMPILER=cl.exe
|
||||
)
|
||||
if DEFINED SANITIZE (
|
||||
@set "SANITIZERS=/fsanitize=address"
|
||||
) else (
|
||||
@set "SANITIZERS="
|
||||
)
|
||||
@set JANET_COMPILE=%COMPILER% /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD %SANITIZERS%
|
||||
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD
|
||||
@set JANET_LINK=link /nologo
|
||||
|
||||
@set JANET_LINK_STATIC=lib /nologo
|
||||
@@ -51,35 +41,34 @@ if not exist build\boot mkdir build\boot
|
||||
@rem Build the bootstrap interpreter
|
||||
for %%f in (src\core\*.c) do (
|
||||
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
)
|
||||
for %%f in (src\boot\*.c) do (
|
||||
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
)
|
||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@rem note that there is no default syspath being baked in
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
build\janet_boot . > build\c\janet.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
|
||||
@rem Build the sources
|
||||
%JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
%JANET_COMPILE% /Fobuild\shell.obj src\mainclient\shell.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
|
||||
@rem Build the resources
|
||||
rc /nologo /fobuild\janet_win.res janet_win.rc
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
|
||||
@rem Link everything to main client
|
||||
%JANET_LINK% /out:janet.exe build\janet.obj build\shell.obj build\janet_win.res
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
|
||||
@rem Build static library (libjanet.lib)
|
||||
%JANET_LINK_STATIC% /out:build\libjanet.lib build\janet.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
|
||||
echo === Successfully built janet.exe for Windows ===
|
||||
echo === Run 'build_win test' to run tests. ==
|
||||
@@ -102,7 +91,7 @@ exit /b 0
|
||||
|
||||
@rem Clean build artifacts
|
||||
:CLEAN
|
||||
del *.exe *.lib *.exp *.msi *.wixpdb
|
||||
del *.exe *.lib *.exp
|
||||
rd /s /q build
|
||||
if exist dist (
|
||||
rd /s /q dist
|
||||
@@ -113,7 +102,7 @@ exit /b 0
|
||||
:TEST
|
||||
for %%f in (test/suite*.janet) do (
|
||||
janet.exe test\%%f
|
||||
@if errorlevel 1 goto TESTFAIL
|
||||
@if not errorlevel 0 goto TESTFAIL
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
@@ -154,13 +143,7 @@ if defined CI (
|
||||
) else (
|
||||
set WIXBIN=
|
||||
)
|
||||
|
||||
set WIXARCH=%BUILDARCH%
|
||||
if "%WIXARCH%"=="aarch64" (
|
||||
set WIXARCH=arm64
|
||||
)
|
||||
|
||||
%WIXBIN%candle.exe tools\msi\janet.wxs -arch %WIXARCH% -out build\
|
||||
%WIXBIN%candle.exe tools\msi\janet.wxs -arch %BUILDARCH% -out build\
|
||||
%WIXBIN%light.exe "-sice:ICE38" -b tools\msi -ext WixUIExtension build\janet.wixobj -out janet-%RELEASE_VERSION%-windows-%BUILDARCH%-installer.msi
|
||||
exit /b 0
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# Linux only - uses abstract unix domain sockets
|
||||
(ev/spawn (net/server :unix "@abc123" (fn [conn] (print (:read conn 1024)) (:close conn))))
|
||||
(ev/sleep 1)
|
||||
(def s (net/connect :unix "@abc123" :stream))
|
||||
(:write s "hello")
|
||||
(:close s)
|
||||
@@ -26,7 +26,7 @@
|
||||
(broadcast name (string msg)))
|
||||
(print name " disconnected")))))
|
||||
|
||||
(defn main [&]
|
||||
(defn main [& args]
|
||||
(printf "STARTING SERVER...")
|
||||
(flush)
|
||||
(def my-server (net/listen "127.0.0.1" "8000"))
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
"Go to the next breakpoint."
|
||||
[&opt n]
|
||||
(var res nil)
|
||||
(repeat (or n 1)
|
||||
(for i 0 (or n 1)
|
||||
(set res (resume (.fiber))))
|
||||
res)
|
||||
|
||||
@@ -146,6 +146,6 @@
|
||||
"Execute the next n instructions."
|
||||
[&opt n]
|
||||
(var res nil)
|
||||
(repeat (or n 1)
|
||||
(for i 0 (or n 1)
|
||||
(set res (debug/step (.fiber))))
|
||||
res)
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
(defn bork [x]
|
||||
|
||||
(defn bark [y]
|
||||
(defn bark [x]
|
||||
(print "Woof!")
|
||||
(print y)
|
||||
(error y)
|
||||
(print x)
|
||||
(error x)
|
||||
(print "Woof!"))
|
||||
|
||||
(bark (* 2 x))
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
(print "simple yielding")
|
||||
(each item f (print "got: " item ", now " (fiber/status f)))
|
||||
|
||||
(def f2
|
||||
(def f
|
||||
(coro
|
||||
(for i 0 10
|
||||
(yield (string "yield " i))
|
||||
(ev/sleep 0))))
|
||||
|
||||
(print "complex yielding")
|
||||
(each item f2 (print "got: " item ", now " (fiber/status f2)))
|
||||
(each item f (print "got: " item ", now " (fiber/status f)))
|
||||
|
||||
(print (fiber/status f2))
|
||||
(print (fiber/status f))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# that must be called (realizing it), and the memoized.
|
||||
# Use with (import "./path/to/this/file" :prefix "seq.")
|
||||
|
||||
(defmacro dolazy
|
||||
(defmacro delay
|
||||
"Lazily evaluate a series of expressions. Returns a function that
|
||||
returns the result of the last expression. Will only evaluate the
|
||||
body once, and then memoizes the result."
|
||||
@@ -35,7 +35,7 @@
|
||||
(def x (tuple h t))
|
||||
(fn [] x))
|
||||
|
||||
(defn lazy-empty?
|
||||
(defn empty?
|
||||
"Check if a sequence is empty."
|
||||
[s]
|
||||
(not (s)))
|
||||
@@ -55,14 +55,14 @@
|
||||
[start end &]
|
||||
(if end
|
||||
(if (< start end)
|
||||
(dolazy (tuple start (lazy-range (+ 1 start) end)))
|
||||
(delay (tuple start (lazy-range (+ 1 start) end)))
|
||||
empty-seq)
|
||||
(lazy-range 0 start)))
|
||||
|
||||
(defn lazy-map
|
||||
"Return a sequence that is the result of applying f to each value in s."
|
||||
[f s]
|
||||
(dolazy
|
||||
(delay
|
||||
(def x (s))
|
||||
(if x (tuple (f (get x HEAD)) (map f (get x TAIL))))))
|
||||
|
||||
@@ -76,31 +76,31 @@
|
||||
[f s]
|
||||
(when (s) (f (head s)) (realize-map f (tail s))))
|
||||
|
||||
(defn lazy-drop
|
||||
(defn drop
|
||||
"Ignores the first n values of the sequence and returns the rest."
|
||||
[n s]
|
||||
(dolazy
|
||||
(delay
|
||||
(def x (s))
|
||||
(if (and x (pos? n)) ((lazy-drop (- n 1) (get x TAIL))))))
|
||||
(if (and x (pos? n)) ((drop (- n 1) (get x TAIL))))))
|
||||
|
||||
(defn lazy-take
|
||||
(defn take
|
||||
"Returns at most the first n values of s."
|
||||
[n s]
|
||||
(dolazy
|
||||
(delay
|
||||
(def x (s))
|
||||
(if (and x (pos? n))
|
||||
(tuple (get x HEAD) (lazy-take (- n 1) (get x TAIL))))))
|
||||
(tuple (get x HEAD) (take (- n 1) (get x TAIL))))))
|
||||
|
||||
(defn randseq
|
||||
"Return a sequence of random numbers."
|
||||
[]
|
||||
(dolazy (tuple (math/random) (randseq))))
|
||||
(delay (tuple (math/random) (randseq))))
|
||||
|
||||
(defn lazy-take-while
|
||||
(defn take-while
|
||||
"Returns a sequence of values until the predicate is false."
|
||||
[pred s]
|
||||
(dolazy
|
||||
(delay
|
||||
(def x (s))
|
||||
(when x
|
||||
(def thehead (get HEAD x))
|
||||
(if thehead (tuple thehead (lazy-take-while pred (get TAIL x)))))))
|
||||
(if thehead (tuple thehead (take-while pred (get TAIL x)))))))
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
(def cell-set (frequencies state))
|
||||
(def neighbor-set (frequencies (mapcat neighbors state)))
|
||||
(seq [coord :keys neighbor-set
|
||||
:let [ncount (get neighbor-set coord)]
|
||||
:when (or (= ncount 3) (and (get cell-set coord) (= ncount 2)))]
|
||||
:let [count (get neighbor-set coord)]
|
||||
:when (or (= count 3) (and (get cell-set coord) (= count 2)))]
|
||||
coord))
|
||||
|
||||
(defn draw
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
(def counts (seq [_ :range [0 100]] 0))
|
||||
|
||||
(repeat 1000000
|
||||
(for i 0 1000000
|
||||
(let [x (math/random)
|
||||
intrange (math/floor (* 100 x))
|
||||
oldcount (counts intrange)]
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@{
|
||||
:name "sample-bad-bundle1"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
@{
|
||||
:name "sample-bad-bundle2"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
@{
|
||||
:name "sample-dep1"
|
||||
:dependencies [{:name "sample-dep2"}]
|
||||
:dependencies ["sample-dep2"]
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
(ev/give chan (math/random))
|
||||
(ev/give chan (math/random))
|
||||
(ev/sleep 0.5)
|
||||
(repeat 10
|
||||
(for i 0 10
|
||||
(print "giving to channel...")
|
||||
(ev/give chan (math/random))
|
||||
(ev/sleep 1))
|
||||
|
||||
13
janet.1
13
janet.1
@@ -156,7 +156,7 @@ Shows the version text and exits immediately.
|
||||
|
||||
.TP
|
||||
.BR \-s
|
||||
Read raw input from stdin and forgo fancy input, which includes prompt history and other readline-like features.
|
||||
Read raw input from stdin and forgo prompt history and other readline-like features.
|
||||
|
||||
.TP
|
||||
.BR \-e\ code
|
||||
@@ -214,7 +214,7 @@ Don't execute a script, only compile it to check for errors. Useful for linting
|
||||
.BR \-m\ syspath
|
||||
Set the dynamic binding :syspath to the string syspath so that Janet will load system modules
|
||||
from a directory different than the default. The default is set when Janet is built, and defaults to
|
||||
/usr/local/lib/janet on Linux/Posix. On Windows, there is no default value. This option supersedes JANET_PATH.
|
||||
/usr/local/lib/janet on Linux/Posix, and C:/Janet/Library on Windows. This option supersedes JANET_PATH.
|
||||
|
||||
.TP
|
||||
.BR \-c\ source\ output
|
||||
@@ -255,7 +255,8 @@ and then arguments to the script.
|
||||
.RS
|
||||
The location to look for Janet libraries. This is the only environment variable Janet needs to
|
||||
find native and source code modules. If no JANET_PATH is set, Janet will look in
|
||||
the default location set at compile time. This should be a colon-separated list of directory names on Linux/Posix, and a semicolon-separated list on Windows. Note that a typical setup (i.e. not NixOS / Guix) will only use a single directory.
|
||||
the default location set at compile time. This should be a list of as well as a colon
|
||||
separate list of such directories.
|
||||
.RE
|
||||
|
||||
.B JANET_PROFILE
|
||||
@@ -272,12 +273,6 @@ This variable does nothing in the default configuration of Janet, as PRF is disa
|
||||
cannot be defined for this variable to have an effect.
|
||||
.RE
|
||||
|
||||
.B JANET_HISTFILE
|
||||
.RS
|
||||
A file location to use for the default shell's REPL history when using fancy input. This relative path is where commands are persisted between sessions.
|
||||
If unset, no repl history well be used. Does not work with the -s flag where fancy input is disabled.
|
||||
.RE
|
||||
|
||||
.B NO_COLOR
|
||||
.RS
|
||||
Turn off color by default in the repl and in the error handler of scripts. This can be changed at runtime
|
||||
|
||||
25
meson.build
25
meson.build
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2026 Calvin Rose and contributors
|
||||
# Copyright (c) 2024 Calvin Rose and contributors
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
project('janet', 'c',
|
||||
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
|
||||
version : '1.41.3')
|
||||
version : '1.37.1')
|
||||
|
||||
# Global settings
|
||||
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
|
||||
@@ -39,15 +39,6 @@ native_thread_dep = dependency('threads', native : true)
|
||||
# Deps
|
||||
m_dep = cc.find_library('m', required : false)
|
||||
dl_dep = cc.find_library('dl', required : false)
|
||||
|
||||
# for MINGW/MSYS2
|
||||
native_ws2_dep = native_cc.find_library('ws2_32', required: false)
|
||||
native_psapi_dep = native_cc.find_library('psapi', required: false)
|
||||
native_wsock_dep = native_cc.find_library('wsock32', required: false)
|
||||
ws2_dep = cc.find_library('ws2_32', required: false)
|
||||
psapi_dep = cc.find_library('psapi', required: false)
|
||||
wsock_dep = cc.find_library('wsock32', required: false)
|
||||
|
||||
android_spawn_dep = cc.find_library('android-spawn', required : false)
|
||||
thread_dep = dependency('threads')
|
||||
|
||||
@@ -72,9 +63,6 @@ conf.set_quoted('JANET_VERSION', meson.project_version())
|
||||
# Use options
|
||||
conf.set_quoted('JANET_BUILD', get_option('git_hash'))
|
||||
conf.set('JANET_NO_NANBOX', not get_option('nanbox'))
|
||||
if get_option('nanbox_pointer_shift') != -1 # -1 is auto-detect
|
||||
conf.set('JANET_NANBOX_64_POINTER_SHIFT', get_option('nanbox_pointer_shift'))
|
||||
endif
|
||||
conf.set('JANET_SINGLE_THREADED', get_option('single_threaded'))
|
||||
conf.set('JANET_NO_DYNAMIC_MODULES', not get_option('dynamic_modules'))
|
||||
conf.set('JANET_NO_DOCSTRINGS', not get_option('docstrings'))
|
||||
@@ -108,9 +96,6 @@ endif
|
||||
if get_option('arch_name') != ''
|
||||
conf.set('JANET_ARCH_NAME', get_option('arch_name'))
|
||||
endif
|
||||
if get_option('thread_local_prefix') != ''
|
||||
conf.set('JANET_THREAD_LOCAL', get_option('thread_local_prefix'))
|
||||
endif
|
||||
jconf = configure_file(output : 'janetconf.h',
|
||||
configuration : conf)
|
||||
|
||||
@@ -188,8 +173,8 @@ mainclient_src = [
|
||||
'src/mainclient/shell.c'
|
||||
]
|
||||
|
||||
janet_dependencies = [m_dep, dl_dep, android_spawn_dep, ws2_dep, psapi_dep, wsock_dep]
|
||||
janet_native_dependencies = [native_m_dep, native_dl_dep, native_android_spawn_dep, native_ws2_dep, native_psapi_dep, native_wsock_dep]
|
||||
janet_dependencies = [m_dep, dl_dep, android_spawn_dep]
|
||||
janet_native_dependencies = [native_m_dep, native_dl_dep, native_android_spawn_dep]
|
||||
if not get_option('single_threaded')
|
||||
janet_dependencies += thread_dep
|
||||
janet_native_dependencies += native_thread_dep
|
||||
@@ -284,14 +269,12 @@ test_files = [
|
||||
'test/suite-corelib.janet',
|
||||
'test/suite-debug.janet',
|
||||
'test/suite-ev.janet',
|
||||
'test/suite-ev2.janet',
|
||||
'test/suite-ffi.janet',
|
||||
'test/suite-filewatch.janet',
|
||||
'test/suite-inttypes.janet',
|
||||
'test/suite-io.janet',
|
||||
'test/suite-marsh.janet',
|
||||
'test/suite-math.janet',
|
||||
'test/suite-net.janet',
|
||||
'test/suite-os.janet',
|
||||
'test/suite-parse.janet',
|
||||
'test/suite-peg.janet',
|
||||
|
||||
@@ -2,7 +2,6 @@ option('git_hash', type : 'string', value : 'meson')
|
||||
|
||||
option('single_threaded', type : 'boolean', value : false)
|
||||
option('nanbox', type : 'boolean', value : true)
|
||||
option('nanbox_pointer_shift', type : 'integer', min : -1, max : 4, value : -1)
|
||||
option('dynamic_modules', type : 'boolean', value : true)
|
||||
option('docstrings', type : 'boolean', value : true)
|
||||
option('sourcemaps', type : 'boolean', value : true)
|
||||
@@ -31,7 +30,6 @@ option('max_macro_expand', type : 'integer', min : 1, max : 8000, value : 200)
|
||||
option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7fffffff)
|
||||
|
||||
option('arch_name', type : 'string', value: '')
|
||||
option('thread_local_prefix', type : 'string', value: '')
|
||||
option('os_name', type : 'string', value: '')
|
||||
option('shared', type : 'boolean', value: true)
|
||||
option('cryptorand', type : 'boolean', value: true)
|
||||
|
||||
49
plan9.mk
49
plan9.mk
@@ -1,49 +0,0 @@
|
||||
</$objtype/mkfile
|
||||
|
||||
TARG=janet
|
||||
HFILES=src/include/janet.h src/conf/janetconf.h
|
||||
JANET_PATH=/sys/lib/janet
|
||||
BIN=/$objtype/bin/
|
||||
DISABLED=EV NET ASSEMBLER FFI UTC_MKTIME REALPATH DYNAMIC_MODULES THREADS SYMLINKS LOCALES UMASK SPAWN
|
||||
JANET_CONFIG=JANET_SINGLE_THREADED JANET_OS_NAME=plan9 JANET_ARCH_NAME=$objtype JANET_API='' JANET_NO_RETURN='' JANET_SIMPLE_GETLINE `{echo JANET_NO_^$DISABLED} PLAN9_`{echo -n $objtype}
|
||||
CFLAGS=-FTVBNcwp -D _POSIX_SOURCE -DJANET_PLAN9 -D_BSD_EXTENSION -D_LIMITS_EXTENSION -Isrc/include -Isrc/conf -I/sys/include/npe -Dtypestr=janettypestr -DJANET_API `{echo '-D'^$JANET_CONFIG}
|
||||
BOOT_CFLAGS=$CFLAGS -DJANET_BOOTSTRAP
|
||||
|
||||
JANET_CORE_HEADERS=`{ls src/core/*.h}
|
||||
JANET_CORE_SOURCES=`{ls src/core/*.c}
|
||||
|
||||
JANET_BOOT_SOURCES=src/boot/array_test.c \
|
||||
src/boot/boot.c \
|
||||
src/boot/buffer_test.c \
|
||||
src/boot/number_test.c \
|
||||
src/boot/system_test.c \
|
||||
src/boot/table_test.c
|
||||
JANET_BOOT_HEADERS=src/boot/tests.h
|
||||
JANET_BOOT_OBJECTS=`{echo $JANET_CORE_SOURCES $JANET_BOOT_SOURCES | sed -e 's/\.c/.boot.'$O'/g'}
|
||||
|
||||
OFILES=janet.$O src/mainclient/shell.$O
|
||||
|
||||
default:V:all
|
||||
|
||||
src/%.boot.$O: src/%.c $JANET_HEADERS $JANET_CORE_HEADERS $JANET_BOOT_HEADERS
|
||||
$CC $BOOT_CFLAGS -o $target $prereq(1)
|
||||
|
||||
src/mainclient/shell.$O: src/mainclient/shell.c
|
||||
$CC $BOOT_CFLAGS -o $target $prereq(1)
|
||||
|
||||
$O.janetboot: $JANET_BOOT_OBJECTS
|
||||
$LD $LDFLAGS -o $target $prereq
|
||||
|
||||
janet.c: $O.janetboot src/boot/boot.janet
|
||||
$prereq(1) . JANET_PATH $JANET_PATH >$target
|
||||
|
||||
build/janet.$O: build/c/janet.c src/conf/janetconf.h src/include/janet.h
|
||||
$CC $CFLAGS -D^$JANET_CONFIG -o $target $prereq(1)
|
||||
|
||||
build/shell.$O: src/mainclient/shell.c src/conf/janetconf.h src/include/janet.h
|
||||
$CC $CFLAGS -D^$JANET_CONFIG -o $target $prereq(1)
|
||||
|
||||
</sys/src/cmd/mkone
|
||||
|
||||
clean:V:
|
||||
rm -f src/core/*.[$OS] src/boot/*.[$OS] *.a[$OS] y.tab.? lex.yy.c y.debug y.output [$OS].??* $TARG janet.[$OS] janet.c src/mainclient/shell.[$OS]
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -44,9 +44,7 @@ static void test_valid_str(const char *str) {
|
||||
}
|
||||
|
||||
int number_test() {
|
||||
#ifdef JANET_PLAN9
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
test_valid_str("1.0");
|
||||
test_valid_str("1");
|
||||
test_valid_str("2.1");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -37,7 +37,7 @@ int system_test() {
|
||||
|
||||
/* Check the version defines are self consistent */
|
||||
char version_combined[256];
|
||||
snprintf(version_combined, sizeof(version_combined), "%d.%d.%d%s", JANET_VERSION_MAJOR, JANET_VERSION_MINOR, JANET_VERSION_PATCH, JANET_VERSION_EXTRA);
|
||||
sprintf(version_combined, "%d.%d.%d%s", JANET_VERSION_MAJOR, JANET_VERSION_MINOR, JANET_VERSION_PATCH, JANET_VERSION_EXTRA);
|
||||
assert(!strcmp(JANET_VERSION, version_combined));
|
||||
|
||||
/* Reflexive testing and nanbox testing */
|
||||
@@ -51,10 +51,6 @@ int system_test() {
|
||||
assert(janet_equals(janet_wrap_number(1.4), janet_wrap_number(1.4)));
|
||||
assert(janet_equals(janet_wrap_number(3.14159265), janet_wrap_number(3.14159265)));
|
||||
#ifdef NAN
|
||||
#ifdef JANET_PLAN9
|
||||
// Plan 9 traps NaNs by default; disable that.
|
||||
setfcr(0);
|
||||
#endif
|
||||
assert(janet_checktype(janet_wrap_number(NAN), JANET_NUMBER));
|
||||
#else
|
||||
assert(janet_checktype(janet_wrap_number(0.0 / 0.0), JANET_NUMBER));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -4,19 +4,17 @@
|
||||
#define JANETCONF_H
|
||||
|
||||
#define JANET_VERSION_MAJOR 1
|
||||
#define JANET_VERSION_MINOR 41
|
||||
#define JANET_VERSION_PATCH 3
|
||||
#define JANET_VERSION_EXTRA "-dev"
|
||||
#define JANET_VERSION "1.41.3-dev"
|
||||
#define JANET_VERSION_MINOR 37
|
||||
#define JANET_VERSION_PATCH 1
|
||||
#define JANET_VERSION_EXTRA ""
|
||||
#define JANET_VERSION "1.37.1"
|
||||
|
||||
/* #define JANET_BUILD "local" */
|
||||
|
||||
/* These settings all affect linking, so use cautiously. */
|
||||
/* #define JANET_SINGLE_THREADED */
|
||||
/* #define JANET_THREAD_LOCAL _Thread_local */
|
||||
/* #define JANET_NO_DYNAMIC_MODULES */
|
||||
/* #define JANET_NO_NANBOX */
|
||||
/* #define JANET_NANBOX_64_POINTER_SHIFT 0 */
|
||||
/* #define JANET_API __attribute__((visibility ("default"))) */
|
||||
|
||||
/* These settings should be specified before amalgamation is
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -201,17 +201,4 @@ int32_t janet_abstract_decref(void *abst) {
|
||||
return janet_atomic_dec(&janet_abstract_head(abst)->gc.data.refcount);
|
||||
}
|
||||
|
||||
int32_t janet_abstract_decref_maybe_free(void *abst) {
|
||||
int32_t result = janet_abstract_decref(abst);
|
||||
if (0 == result) {
|
||||
JanetAbstractHead *head = janet_abstract_head(abst);
|
||||
if (head->type->gc) {
|
||||
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
|
||||
}
|
||||
/* Free memory */
|
||||
janet_free(head);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -567,13 +567,6 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
x = janet_get1(s, janet_ckeywordv("structarg"));
|
||||
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
|
||||
|
||||
/* Check namedarg */
|
||||
x = janet_get1(s, janet_ckeywordv("namedargs"));
|
||||
if (janet_checkint(x)) {
|
||||
def->flags |= JANET_FUNCDEF_FLAG_NAMEDARGS;
|
||||
def->named_args_count = janet_unwrap_integer(x);
|
||||
}
|
||||
|
||||
/* Check source */
|
||||
x = janet_get1(s, janet_ckeywordv("source"));
|
||||
if (janet_checktype(x, JANET_STRING)) def->source = janet_unwrap_string(x);
|
||||
@@ -989,14 +982,6 @@ static Janet janet_disasm_structarg(JanetFuncDef *def) {
|
||||
return janet_wrap_boolean(def->flags & JANET_FUNCDEF_FLAG_STRUCTARG);
|
||||
}
|
||||
|
||||
static Janet janet_disasm_namedargs(JanetFuncDef *def) {
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_NAMEDARGS) {
|
||||
return janet_wrap_integer(def->named_args_count);
|
||||
} else {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
}
|
||||
|
||||
static Janet janet_disasm_constants(JanetFuncDef *def) {
|
||||
JanetArray *constants = janet_array(def->constants_length);
|
||||
for (int32_t i = 0; i < def->constants_length; i++) {
|
||||
@@ -1047,7 +1032,6 @@ Janet janet_disasm(JanetFuncDef *def) {
|
||||
janet_table_put(ret, janet_ckeywordv("source"), janet_disasm_source(def));
|
||||
janet_table_put(ret, janet_ckeywordv("vararg"), janet_disasm_vararg(def));
|
||||
janet_table_put(ret, janet_ckeywordv("structarg"), janet_disasm_structarg(def));
|
||||
janet_table_put(ret, janet_ckeywordv("namedargs"), janet_disasm_namedargs(def));
|
||||
janet_table_put(ret, janet_ckeywordv("name"), janet_disasm_name(def));
|
||||
janet_table_put(ret, janet_ckeywordv("slotcount"), janet_disasm_slotcount(def));
|
||||
janet_table_put(ret, janet_ckeywordv("symbolmap"), janet_disasm_symbolslots(def));
|
||||
@@ -1064,7 +1048,6 @@ JANET_CORE_FN(cfun_asm,
|
||||
"The syntax for the assembly can be found on the Janet website, and should correspond\n"
|
||||
"to the return value of disasm. Will throw an\n"
|
||||
"error on invalid assembly.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_ASM);
|
||||
janet_fixarity(argc, 1);
|
||||
JanetAssembleResult res;
|
||||
res = janet_asm(argv[0], 0);
|
||||
@@ -1084,8 +1067,6 @@ JANET_CORE_FN(cfun_disasm,
|
||||
"* :min-arity - minimum number of arguments function can be called with.\n"
|
||||
"* :max-arity - maximum number of arguments function can be called with.\n"
|
||||
"* :vararg - true if function can take a variable number of arguments.\n"
|
||||
"* :structarg - true if function can take a variable number of arguments using the &keys option.\n"
|
||||
"* :namedargs - if function can take a variable number of arguments using the &named option, this will be the number of named arguments.\n"
|
||||
"* :bytecode - array of parsed bytecode instructions. Each instruction is a tuple.\n"
|
||||
"* :source - name of source file that this function was compiled from.\n"
|
||||
"* :name - name of function.\n"
|
||||
@@ -1095,7 +1076,6 @@ JANET_CORE_FN(cfun_disasm,
|
||||
"* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n"
|
||||
"* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n"
|
||||
"* :defs - other function definitions that this function may instantiate.\n") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_ASM);
|
||||
janet_arity(argc, 1, 2);
|
||||
JanetFunction *f = janet_getfunction(argv, 0);
|
||||
if (argc == 2) {
|
||||
@@ -1108,9 +1088,7 @@ JANET_CORE_FN(cfun_disasm,
|
||||
if (!janet_cstrcmp(kw, "name")) return janet_disasm_name(f->def);
|
||||
if (!janet_cstrcmp(kw, "vararg")) return janet_disasm_vararg(f->def);
|
||||
if (!janet_cstrcmp(kw, "structarg")) return janet_disasm_structarg(f->def);
|
||||
if (!janet_cstrcmp(kw, "namedargs")) return janet_disasm_namedargs(f->def);
|
||||
if (!janet_cstrcmp(kw, "slotcount")) return janet_disasm_slotcount(f->def);
|
||||
if (!janet_cstrcmp(kw, "symbolmap")) return janet_disasm_symbolslots(f->def);
|
||||
if (!janet_cstrcmp(kw, "constants")) return janet_disasm_constants(f->def);
|
||||
if (!janet_cstrcmp(kw, "sourcemap")) return janet_disasm_sourcemap(f->def);
|
||||
if (!janet_cstrcmp(kw, "environments")) return janet_disasm_environments(f->def);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -74,7 +74,6 @@ JanetBuffer *janet_pointer_buffer_unsafe(void *memory, int32_t capacity, int32_t
|
||||
void janet_buffer_deinit(JanetBuffer *buffer) {
|
||||
if (!(buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC)) {
|
||||
janet_free(buffer->data);
|
||||
buffer->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -29,7 +29,7 @@
|
||||
#endif
|
||||
|
||||
/* Look up table for instructions */
|
||||
const enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
|
||||
enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
|
||||
JINT_0, /* JOP_NOOP, */
|
||||
JINT_S, /* JOP_ERROR, */
|
||||
JINT_ST, /* JOP_TYPECHECK, */
|
||||
@@ -522,7 +522,6 @@ JanetFuncDef *janet_funcdef_alloc(void) {
|
||||
def->bytecode_length = 0;
|
||||
def->environments_length = 0;
|
||||
def->symbolmap_length = 0;
|
||||
def->named_args_count = 0;
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -64,11 +64,6 @@ void janet_signalv(JanetSignal sig, Janet message) {
|
||||
if (janet_vm.return_reg != NULL) {
|
||||
/* Should match logic in janet_call for coercing everything not ok to an error (no awaits, yields, etc.) */
|
||||
if (janet_vm.coerce_error && sig != JANET_SIGNAL_OK) {
|
||||
#ifdef JANET_EV
|
||||
if (NULL != janet_vm.root_fiber && sig == JANET_SIGNAL_EVENT) {
|
||||
janet_vm.root_fiber->sched_id++;
|
||||
}
|
||||
#endif
|
||||
if (sig != JANET_SIGNAL_ERROR) {
|
||||
message = janet_wrap_string(janet_formatc("%v coerced from %s to error", message, janet_signal_names[sig]));
|
||||
}
|
||||
@@ -460,7 +455,7 @@ Janet janet_dyn(const char *name) {
|
||||
return janet_table_get(janet_vm.top_dyns, janet_ckeywordv(name));
|
||||
}
|
||||
if (janet_vm.fiber->env) {
|
||||
return janet_table_get_keyword(janet_vm.fiber->env, name);
|
||||
return janet_table_get(janet_vm.fiber->env, janet_ckeywordv(name));
|
||||
} else {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
@@ -557,18 +552,6 @@ void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetA
|
||||
return janet_getabstract(argv, n, at);
|
||||
}
|
||||
|
||||
uint32_t janet_optuinteger(const Janet *argv, int32_t argc, int32_t n, uint32_t dflt) {
|
||||
if (argc <= n) return dflt;
|
||||
if (janet_checktype(argv[n], JANET_NIL)) return dflt;
|
||||
return janet_getuinteger(argv, n);
|
||||
}
|
||||
|
||||
uint64_t janet_optuinteger64(const Janet *argv, int32_t argc, int32_t n, uint64_t dflt) {
|
||||
if (argc <= n) return dflt;
|
||||
if (janet_checktype(argv[n], JANET_NIL)) return dflt;
|
||||
return janet_getuinteger64(argv, n);
|
||||
}
|
||||
|
||||
/* Atomic refcounts */
|
||||
|
||||
JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) {
|
||||
@@ -576,8 +559,6 @@ JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) {
|
||||
return _InterlockedIncrement(x);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_fetch_add_explicit(x, 1, memory_order_relaxed) + 1;
|
||||
#elif defined(JANET_PLAN9)
|
||||
return aincl((void*)x, 1);
|
||||
#else
|
||||
return __atomic_add_fetch(x, 1, __ATOMIC_RELAXED);
|
||||
#endif
|
||||
@@ -588,8 +569,6 @@ JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) {
|
||||
return _InterlockedDecrement(x);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_fetch_add_explicit(x, -1, memory_order_acq_rel) - 1;
|
||||
#elif defined(JANET_PLAN9)
|
||||
return aincl((void*)x, -1);
|
||||
#else
|
||||
return __atomic_add_fetch(x, -1, __ATOMIC_ACQ_REL);
|
||||
#endif
|
||||
@@ -598,8 +577,6 @@ JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) {
|
||||
JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
|
||||
#ifdef _MSC_VER
|
||||
return _InterlockedOr(x, 0);
|
||||
#elif defined(JANET_PLAN9)
|
||||
return agetl((void*)x);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_load_explicit(x, memory_order_acquire);
|
||||
#else
|
||||
@@ -607,18 +584,6 @@ JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
|
||||
#endif
|
||||
}
|
||||
|
||||
JanetAtomicInt janet_atomic_load_relaxed(JanetAtomicInt volatile *x) {
|
||||
#ifdef _MSC_VER
|
||||
return _InterlockedOr(x, 0);
|
||||
#elif defined(JANET_PLAN9)
|
||||
return agetl((void*)x);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_load_explicit(x, memory_order_relaxed);
|
||||
#else
|
||||
return __atomic_load_n(x, __ATOMIC_RELAXED);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Some definitions for function-like macros */
|
||||
|
||||
JANET_API JanetStructHead *(janet_struct_head)(JanetStruct st) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -201,29 +201,14 @@ static JanetSlot do_cmp(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil(), janet_wrap_nil());
|
||||
}
|
||||
static JanetSlot do_put(JanetFopts opts, JanetSlot *args) {
|
||||
int8_t inline_index = 0;
|
||||
if (can_slot_be_imm(args[1], &inline_index)) {
|
||||
/* Use JOP_PUT_INDEX */
|
||||
if (opts.flags & JANET_FOPTS_DROP) {
|
||||
janetc_emit_ssi(opts.compiler, JOP_PUT_INDEX, args[0], args[2], inline_index, 0);
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
} else {
|
||||
JanetSlot t = janetc_gettarget(opts);
|
||||
janetc_copy(opts.compiler, t, args[0]);
|
||||
janetc_emit_ssi(opts.compiler, JOP_PUT_INDEX, t, args[2], inline_index, 0);
|
||||
return t;
|
||||
}
|
||||
if (opts.flags & JANET_FOPTS_DROP) {
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, args[0], args[1], args[2], 0);
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
} else {
|
||||
/* Use JOP_PUT */
|
||||
if (opts.flags & JANET_FOPTS_DROP) {
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, args[0], args[1], args[2], 0);
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
} else {
|
||||
JanetSlot t = janetc_gettarget(opts);
|
||||
janetc_copy(opts.compiler, t, args[0]);
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, t, args[1], args[2], 0);
|
||||
return t;
|
||||
}
|
||||
JanetSlot t = janetc_gettarget(opts);
|
||||
janetc_copy(opts.compiler, t, args[0]);
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, t, args[1], args[2], 0);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
static JanetSlot do_length(JanetFopts opts, JanetSlot *args) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -91,38 +91,13 @@ void janetc_freeslot(JanetCompiler *c, JanetSlot s) {
|
||||
}
|
||||
|
||||
/* Add a slot to a scope with a symbol associated with it (def or var). */
|
||||
void janetc_nameslot(JanetCompiler *c, const uint8_t *sym, JanetSlot s, uint32_t flags) {
|
||||
if (!(flags & JANET_DEFFLAG_NO_SHADOWCHECK)) {
|
||||
if (sym[0] != '_') {
|
||||
switch (janetc_shadowcheck(c, sym)) {
|
||||
default:
|
||||
break;
|
||||
case JANETC_SHADOW_MACRO:
|
||||
janetc_lintf(c, JANET_C_LINT_NORMAL, "binding %q is shadowing a macro", janet_wrap_symbol(sym));
|
||||
break;
|
||||
case JANETC_SHADOW_LOCAL_HIDES_LOCAL:
|
||||
janetc_lintf(c, JANET_C_LINT_STRICT, "binding %q is shadowing a binding", janet_wrap_symbol(sym));
|
||||
break;
|
||||
case JANETC_SHADOW_LOCAL_HIDES_GLOBAL:
|
||||
janetc_lintf(c, JANET_C_LINT_STRICT, "binding %q is shadowing a top-level binding", janet_wrap_symbol(sym));
|
||||
break;
|
||||
case JANETC_SHADOW_GLOBAL_HIDES_GLOBAL:
|
||||
janetc_lintf(c, JANET_C_LINT_STRICT, "top-level binding %q is shadowing another top-level binding", janet_wrap_symbol(sym));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void janetc_nameslot(JanetCompiler *c, const uint8_t *sym, JanetSlot s) {
|
||||
SymPair sp;
|
||||
int32_t cnt = janet_v_count(c->buffer);
|
||||
sp.sym = sym;
|
||||
sp.sym2 = sym;
|
||||
sp.slot = s;
|
||||
sp.keep = 0;
|
||||
if (flags & JANET_DEFFLAG_NO_UNUSED) {
|
||||
sp.referenced = 1;
|
||||
} else {
|
||||
sp.referenced = sym[0] == '_'; /* Fake ref if symbol starts with _ to avoid lints */
|
||||
}
|
||||
sp.slot.flags |= JANET_SLOT_NAMED;
|
||||
sp.birth_pc = cnt ? cnt - 1 : 0;
|
||||
sp.death_pc = UINT32_MAX;
|
||||
@@ -195,10 +170,6 @@ void janetc_popscope(JanetCompiler *c) {
|
||||
/* Keep upvalue slots and symbols for debugging. */
|
||||
for (int32_t i = 0; i < janet_v_count(oldscope->syms); i++) {
|
||||
SymPair pair = oldscope->syms[i];
|
||||
/* Check for unused symbols */
|
||||
if (pair.referenced == 0 && pair.sym) {
|
||||
janetc_lintf(c, JANET_C_LINT_STRICT, "binding %q is unused", janet_wrap_symbol(pair.sym));
|
||||
}
|
||||
/* The variable should not be lexically accessible */
|
||||
pair.sym = NULL;
|
||||
if (pair.death_pc == UINT32_MAX) {
|
||||
@@ -269,38 +240,6 @@ static int lookup_missing(
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check if a binding is defined in an upper scope. This lets us check for
|
||||
* variable shadowing. */
|
||||
Shadowing janetc_shadowcheck(JanetCompiler *c, const uint8_t *sym) {
|
||||
/* Check locals */
|
||||
JanetScope *scope = c->scope;
|
||||
int is_global = (scope->flags & JANET_SCOPE_TOP);
|
||||
while (scope) {
|
||||
int32_t len = janet_v_count(scope->syms);
|
||||
for (int32_t i = len - 1; i >= 0; i--) {
|
||||
SymPair *pair = scope->syms + i;
|
||||
if (pair->sym == sym) {
|
||||
janet_assert(!is_global, "shadowing analysis is incorrect. compiler bug");
|
||||
return JANETC_SHADOW_LOCAL_HIDES_LOCAL;
|
||||
}
|
||||
}
|
||||
scope = scope->parent;
|
||||
}
|
||||
/* Check globals */
|
||||
JanetBinding binding = janet_resolve_ext(c->env, sym);
|
||||
if (binding.type == JANET_BINDING_MACRO || binding.type == JANET_BINDING_DYNAMIC_MACRO) {
|
||||
return JANETC_SHADOW_MACRO;
|
||||
} else if (binding.type == JANET_BINDING_NONE) {
|
||||
return JANETC_SHADOW_NONE;
|
||||
} else {
|
||||
if (is_global) {
|
||||
return JANETC_SHADOW_GLOBAL_HIDES_GLOBAL;
|
||||
} else {
|
||||
return JANETC_SHADOW_LOCAL_HIDES_GLOBAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Allow searching for symbols. Return information about the symbol */
|
||||
JanetSlot janetc_resolve(
|
||||
JanetCompiler *c,
|
||||
@@ -323,7 +262,6 @@ JanetSlot janetc_resolve(
|
||||
pair = scope->syms + i;
|
||||
if (pair->sym == sym) {
|
||||
ret = pair->slot;
|
||||
pair->referenced = 1;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
@@ -336,7 +274,7 @@ JanetSlot janetc_resolve(
|
||||
{
|
||||
JanetBinding binding = janet_resolve_ext(c->env, sym);
|
||||
if (binding.type == JANET_BINDING_NONE) {
|
||||
Janet handler = janet_table_get_keyword(c->env, "missing-symbol");
|
||||
Janet handler = janet_table_get(c->env, janet_ckeywordv("missing-symbol"));
|
||||
switch (janet_type(handler)) {
|
||||
case JANET_NIL:
|
||||
break;
|
||||
@@ -408,7 +346,6 @@ found:
|
||||
/* non-local scope needs to expose its environment */
|
||||
JanetScope *original_scope = scope;
|
||||
pair->keep = 1;
|
||||
pair->referenced = 1;
|
||||
while (scope && !(scope->flags & JANET_SCOPE_FUNCTION))
|
||||
scope = scope->parent;
|
||||
janet_assert(scope, "invalid scopes");
|
||||
@@ -500,22 +437,11 @@ JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds) {
|
||||
const JanetKV *kvs = NULL;
|
||||
int32_t cap = 0, len = 0;
|
||||
janet_dictionary_view(ds, &kvs, &len, &cap);
|
||||
/* Sort keys for stability of order? */
|
||||
int32_t *index_buf;
|
||||
int32_t index_buf_stack[32];
|
||||
int32_t *index_buf_heap = NULL;
|
||||
if (len < 32) {
|
||||
index_buf = index_buf_stack;
|
||||
} else {
|
||||
index_buf_heap = janet_smalloc(sizeof(int32_t) * len);
|
||||
index_buf = index_buf_heap;
|
||||
for (int32_t i = 0; i < cap; i++) {
|
||||
if (janet_checktype(kvs[i].key, JANET_NIL)) continue;
|
||||
janet_v_push(ret, janetc_value(subopts, kvs[i].key));
|
||||
janet_v_push(ret, janetc_value(subopts, kvs[i].value));
|
||||
}
|
||||
if (len) janet_sorted_keys(kvs, cap, index_buf);
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
janet_v_push(ret, janetc_value(subopts, kvs[index_buf[i]].key));
|
||||
janet_v_push(ret, janetc_value(subopts, kvs[index_buf[i]].value));
|
||||
}
|
||||
if (index_buf_heap) janet_sfree(index_buf_heap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -588,9 +514,9 @@ void janetc_throwaway(JanetFopts opts, Janet x) {
|
||||
JanetScope unusedScope;
|
||||
int32_t bufstart = janet_v_count(c->buffer);
|
||||
int32_t mapbufstart = janet_v_count(c->mapbuffer);
|
||||
janetc_scope(&unusedScope, c, JANET_SCOPE_UNUSED, "unused");
|
||||
janetc_scope(&unusedScope, c, JANET_SCOPE_UNUSED, "unusued");
|
||||
janetc_value(opts, x);
|
||||
janetc_lintf(c, JANET_C_LINT_STRICT, "dead code, consider removing %.4q", x);
|
||||
janetc_lintf(c, JANET_C_LINT_STRICT, "dead code, consider removing %.2q", x);
|
||||
janetc_popscope(c);
|
||||
if (c->buffer) {
|
||||
janet_v__cnt(c->buffer) = bufstart;
|
||||
@@ -600,7 +526,7 @@ void janetc_throwaway(JanetFopts opts, Janet x) {
|
||||
}
|
||||
|
||||
/* Compile a call or tailcall instruction */
|
||||
static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun, const Janet *form) {
|
||||
static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun) {
|
||||
JanetSlot retslot;
|
||||
JanetCompiler *c = opts.compiler;
|
||||
int specialized = 0;
|
||||
@@ -626,8 +552,6 @@ static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun, c
|
||||
JanetFunction *f = janet_unwrap_function(fun.constant);
|
||||
int32_t min = f->def->min_arity;
|
||||
int32_t max = f->def->max_arity;
|
||||
int structarg = f->def->flags & JANET_FUNCDEF_FLAG_STRUCTARG;
|
||||
int namedarg = f->def->flags & JANET_FUNCDEF_FLAG_NAMEDARGS;
|
||||
if (min_arity < 0) {
|
||||
/* Call has splices */
|
||||
min_arity = -1 - min_arity;
|
||||
@@ -651,47 +575,6 @@ static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun, c
|
||||
fun.constant, min, min == 1 ? "" : "s", min_arity);
|
||||
janetc_error(c, es);
|
||||
}
|
||||
if (structarg && (min_arity > f->def->arity) && ((min_arity - f->def->arity) & 1)) {
|
||||
/* If we have an odd number of variadic arguments to a `&keys` function, that is almost certainly wrong. */
|
||||
if (namedarg) {
|
||||
janetc_lintf(c, JANET_C_LINT_NORMAL,
|
||||
"odd number of named arguments to `&named` function %v", fun.constant);
|
||||
} else {
|
||||
janetc_lintf(c, JANET_C_LINT_NORMAL,
|
||||
"odd number of named arguments to `&keys` function %v", fun.constant);
|
||||
}
|
||||
}
|
||||
if (namedarg && f->def->named_args_count > 0) {
|
||||
/* For each argument passed in, check if it is one of the used named arguments
|
||||
* by checking the list defined in the function def. If not, raise a normal compiler
|
||||
* lint. We can also do a strict lint for _missing_ named arguments, although in many
|
||||
* cases those are assumed to have some kind of default, or we have dynamic keys. */
|
||||
int32_t first_arg_key_index = f->def->arity + 1;
|
||||
for (int32_t i = first_arg_key_index; i < janet_tuple_length(form); i += 2) {
|
||||
Janet argkey = form[i];
|
||||
/* Assumption: The first N constants of a function are its named argument keys. This
|
||||
* may change if the compiler changes, but is true for all Janet generated functions. */
|
||||
int found = 0;
|
||||
if (janet_checktype(argkey, JANET_KEYWORD)) {
|
||||
for (int32_t j = 0; j < f->def->named_args_count && j < f->def->constants_length; j++) {
|
||||
if (janet_equals(argkey, f->def->constants[j])) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (janet_checktype(argkey, JANET_TUPLE)) {
|
||||
/* Possible lint : too dynamic, be dumber
|
||||
* (defn f [&named x] [x])
|
||||
* (f (if (coin-flip) :x :w) 10)
|
||||
* A tuple could be a function call the evaluates to a valid key */
|
||||
found = 1;
|
||||
}
|
||||
if (!found) {
|
||||
janetc_lintf(c, JANET_C_LINT_NORMAL,
|
||||
"unused named argument %v to function %v", argkey, fun.constant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -926,16 +809,14 @@ JanetSlot janetc_value(JanetFopts opts, Janet x) {
|
||||
} else if (janet_tuple_flag(tup) & JANET_TUPLE_FLAG_BRACKETCTOR) { /* [] tuples are not function call */
|
||||
ret = janetc_tuple(opts, x);
|
||||
} else {
|
||||
/* Function calls */
|
||||
JanetSlot head = janetc_value(subopts, tup[0]);
|
||||
subopts.flags = JANET_FUNCTION | JANET_CFUNCTION;
|
||||
ret = janetc_call(opts, janetc_toslots(c, tup + 1, janet_tuple_length(tup) - 1), head, tup);
|
||||
ret = janetc_call(opts, janetc_toslots(c, tup + 1, janet_tuple_length(tup) - 1), head);
|
||||
janetc_freeslot(c, head);
|
||||
}
|
||||
ret.flags &= ~JANET_SLOT_SPLICED;
|
||||
}
|
||||
break;
|
||||
/* Data Constructors */
|
||||
case JANET_SYMBOL:
|
||||
ret = janetc_resolve(c, janet_unwrap_symbol(x));
|
||||
break;
|
||||
@@ -975,21 +856,19 @@ void janet_def_addflags(JanetFuncDef *def) {
|
||||
int32_t set_flags = 0;
|
||||
int32_t unset_flags = 0;
|
||||
/* pos checks */
|
||||
if (def->name) set_flags |= JANET_FUNCDEF_FLAG_HASNAME;
|
||||
if (def->source) set_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
|
||||
if (def->defs) set_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
|
||||
if (def->environments) set_flags |= JANET_FUNCDEF_FLAG_HASENVS;
|
||||
if (def->sourcemap) set_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
|
||||
if (def->closure_bitset) set_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
|
||||
if (def->named_args_count) set_flags |= JANET_FUNCDEF_FLAG_NAMEDARGS;
|
||||
if (def->name) set_flags |= JANET_FUNCDEF_FLAG_HASNAME;
|
||||
if (def->source) set_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
|
||||
if (def->defs) set_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
|
||||
if (def->environments) set_flags |= JANET_FUNCDEF_FLAG_HASENVS;
|
||||
if (def->sourcemap) set_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
|
||||
if (def->closure_bitset) set_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
|
||||
/* negative checks */
|
||||
if (!def->name) unset_flags |= JANET_FUNCDEF_FLAG_HASNAME;
|
||||
if (!def->source) unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
|
||||
if (!def->defs) unset_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
|
||||
if (!def->environments) unset_flags |= JANET_FUNCDEF_FLAG_HASENVS;
|
||||
if (!def->sourcemap) unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
|
||||
if (!def->closure_bitset) unset_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
|
||||
if (!def->named_args_count) unset_flags |= JANET_FUNCDEF_FLAG_NAMEDARGS;
|
||||
if (!def->name) unset_flags |= JANET_FUNCDEF_FLAG_HASNAME;
|
||||
if (!def->source) unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
|
||||
if (!def->defs) unset_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
|
||||
if (!def->environments) unset_flags |= JANET_FUNCDEF_FLAG_HASENVS;
|
||||
if (!def->sourcemap) unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
|
||||
if (!def->closure_bitset) unset_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
|
||||
/* Update flags */
|
||||
def->flags |= set_flags;
|
||||
def->flags &= ~unset_flags;
|
||||
@@ -1060,9 +939,8 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
memcpy(chunks, scope->ua.chunks, sizeof(uint32_t) * numchunks);
|
||||
/* fprintf(stderr, "slot chunks: %d, scope->ua.count: %d, numchunks: %d\n", slotchunks, scope->ua.count, numchunks); */
|
||||
/* Register allocator preallocates some registers [240-255, high 16 bits of chunk index 7], we can ignore those. */
|
||||
if (scope->ua.count > 7 && slotchunks > 7) chunks[7] &= 0xFFFFU;
|
||||
if (scope->ua.count > 7) chunks[7] &= 0xFFFFU;
|
||||
def->closure_bitset = chunks;
|
||||
}
|
||||
|
||||
@@ -1096,10 +974,6 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
|
||||
SymPair pair = scope->syms[i];
|
||||
if (pair.sym2) {
|
||||
JanetSymbolMap jsm;
|
||||
/* Check for unused symbols */
|
||||
if (pair.referenced == 0 && pair.sym) {
|
||||
janetc_lintf(c, JANET_C_LINT_STRICT, "binding %q is unused", janet_wrap_symbol(pair.sym));
|
||||
}
|
||||
if (pair.death_pc == UINT32_MAX) {
|
||||
jsm.death_pc = def->bytecode_length;
|
||||
} else {
|
||||
@@ -1144,7 +1018,6 @@ static void janetc_init(JanetCompiler *c, JanetTable *env, const uint8_t *where,
|
||||
c->current_mapping.line = -1;
|
||||
c->current_mapping.column = -1;
|
||||
c->lints = lints;
|
||||
c->is_redef = janet_truthy(janet_table_get_keyword(c->env, "redef"));
|
||||
/* Init result */
|
||||
c->result.error = NULL;
|
||||
c->result.status = JANET_COMPILE_OK;
|
||||
@@ -1209,7 +1082,6 @@ JANET_CORE_FN(cfun_compile,
|
||||
"struct with keys :line, :column, and :error if compilation fails. "
|
||||
"If a `lints` array is given, linting messages will be appended to the array. "
|
||||
"Each message will be a tuple of the form `(level line col message)`.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_COMPILE);
|
||||
janet_arity(argc, 1, 4);
|
||||
JanetTable *env = (argc > 1 && !janet_checktype(argv[1], JANET_NIL))
|
||||
? janet_gettable(argv, 1) : janet_vm.fiber->env;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -36,15 +36,6 @@ typedef enum {
|
||||
JANET_C_LINT_STRICT
|
||||
} JanetCompileLintLevel;
|
||||
|
||||
/* Kinds of variable shadowing for linting */
|
||||
typedef enum {
|
||||
JANETC_SHADOW_NONE,
|
||||
JANETC_SHADOW_MACRO,
|
||||
JANETC_SHADOW_GLOBAL_HIDES_GLOBAL,
|
||||
JANETC_SHADOW_LOCAL_HIDES_GLOBAL,
|
||||
JANETC_SHADOW_LOCAL_HIDES_LOCAL
|
||||
} Shadowing;
|
||||
|
||||
/* Tags for some functions for the prepared inliner */
|
||||
#define JANET_FUN_DEBUG 1
|
||||
#define JANET_FUN_ERROR 2
|
||||
@@ -123,7 +114,6 @@ typedef struct SymPair {
|
||||
const uint8_t *sym;
|
||||
const uint8_t *sym2;
|
||||
int keep;
|
||||
int referenced; /* Has this value been used */
|
||||
uint32_t birth_pc;
|
||||
uint32_t death_pc;
|
||||
} SymPair;
|
||||
@@ -193,9 +183,6 @@ struct JanetCompiler {
|
||||
|
||||
/* Collect linting results */
|
||||
JanetArray *lints;
|
||||
|
||||
/* Cached version of (dyn *redef*) */
|
||||
int is_redef;
|
||||
};
|
||||
|
||||
#define JANET_FOPTS_TAIL 0x10000
|
||||
@@ -233,11 +220,8 @@ const JanetFunOptimizer *janetc_funopt(uint32_t flags);
|
||||
/* Get a special. Return NULL if none exists */
|
||||
const JanetSpecial *janetc_special(const uint8_t *name);
|
||||
|
||||
#define JANET_DEFFLAG_NO_SHADOWCHECK 1
|
||||
#define JANET_DEFFLAG_NO_UNUSED 2
|
||||
|
||||
void janetc_freeslot(JanetCompiler *c, JanetSlot s);
|
||||
void janetc_nameslot(JanetCompiler *c, const uint8_t *sym, JanetSlot s, uint32_t flags);
|
||||
void janetc_nameslot(JanetCompiler *c, const uint8_t *sym, JanetSlot s);
|
||||
JanetSlot janetc_farslot(JanetCompiler *c);
|
||||
|
||||
/* Throw away some code after checking that it is well formed. */
|
||||
@@ -281,12 +265,9 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c);
|
||||
/* Create a destroy slot */
|
||||
JanetSlot janetc_cslot(Janet x);
|
||||
|
||||
/* Search for a symbol, and mark any found symbols as "used" for dead code elimination and linting */
|
||||
/* Search for a symbol */
|
||||
JanetSlot janetc_resolve(JanetCompiler *c, const uint8_t *sym);
|
||||
|
||||
/* Check if a symbol is already in scope for shadowing lints */
|
||||
Shadowing janetc_shadowcheck(JanetCompiler *c, const uint8_t *sym);
|
||||
|
||||
/* Bytecode optimization */
|
||||
void janet_bytecode_movopt(JanetFuncDef *def);
|
||||
void janet_bytecode_remove_noops(JanetFuncDef *def);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "compile.h"
|
||||
#include "state.h"
|
||||
#include "util.h"
|
||||
#include "fiber.h"
|
||||
#endif
|
||||
|
||||
/* Generated bytes */
|
||||
@@ -67,10 +66,10 @@ JanetModule janet_native(const char *name, const uint8_t **error) {
|
||||
JanetBuildConfig modconf = getter();
|
||||
JanetBuildConfig host = janet_config_current();
|
||||
if (host.major != modconf.major ||
|
||||
host.minor != modconf.minor ||
|
||||
host.minor < modconf.minor ||
|
||||
host.bits != modconf.bits) {
|
||||
char errbuf[128];
|
||||
snprintf(errbuf, sizeof(errbuf), "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x) - native needs to be recompiled!",
|
||||
snprintf(errbuf, sizeof(errbuf), "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)",
|
||||
host.major,
|
||||
host.minor,
|
||||
host.patch,
|
||||
@@ -295,7 +294,6 @@ JANET_CORE_FN(janet_core_native,
|
||||
"from the native module.") {
|
||||
JanetModule init;
|
||||
janet_arity(argc, 1, 2);
|
||||
Janet argv0 = argv[0];
|
||||
const uint8_t *path = janet_getstring(argv, 0);
|
||||
const uint8_t *error = NULL;
|
||||
JanetTable *env;
|
||||
@@ -308,10 +306,8 @@ JANET_CORE_FN(janet_core_native,
|
||||
if (!init) {
|
||||
janet_panicf("could not load native %S: %S", path, error);
|
||||
}
|
||||
/* GC root incase garbage collection called inside module entry */
|
||||
janet_fiber_push(janet_vm.fiber, janet_wrap_table(env));
|
||||
init(env);
|
||||
janet_table_put(env, janet_ckeywordv("native"), argv0);
|
||||
janet_table_put(env, janet_ckeywordv("native"), argv[0]);
|
||||
return janet_wrap_table(env);
|
||||
}
|
||||
|
||||
@@ -453,9 +449,8 @@ JANET_CORE_FN(janet_core_range,
|
||||
}
|
||||
count = (count > 0) ? count : 0;
|
||||
int32_t int_count;
|
||||
janet_assert(count >= 0, "bad range code");
|
||||
if (count > (double) INT32_MAX) {
|
||||
janet_panicf("range is too large, %f elements", count);
|
||||
int_count = INT32_MAX;
|
||||
} else {
|
||||
int_count = (int32_t) ceil(count);
|
||||
}
|
||||
@@ -657,15 +652,22 @@ JANET_CORE_FN(janet_core_check_int,
|
||||
"(int? x)",
|
||||
"Check if x can be exactly represented as a 32 bit signed two's complement integer.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_boolean(janet_checkint(argv[0]));
|
||||
if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
|
||||
double num = janet_unwrap_number(argv[0]);
|
||||
return janet_wrap_boolean(num == (double)((int32_t)num));
|
||||
ret_false:
|
||||
return janet_wrap_false();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_check_nat,
|
||||
"(nat? x)",
|
||||
"Check if x can be exactly represented as a non-negative 32 bit signed two's complement integer.") {
|
||||
janet_fixarity(argc, 1);
|
||||
if (!janet_checkint(argv[0])) return janet_wrap_false();
|
||||
return janet_wrap_boolean(janet_unwrap_integer(argv[0]) >= 0);
|
||||
if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
|
||||
double num = janet_unwrap_number(argv[0]);
|
||||
return janet_wrap_boolean(num >= 0 && (num == (double)((int32_t)num)));
|
||||
ret_false:
|
||||
return janet_wrap_false();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_is_bytes,
|
||||
@@ -750,9 +752,6 @@ typedef struct SandboxOption {
|
||||
|
||||
static const SandboxOption sandbox_options[] = {
|
||||
{"all", JANET_SANDBOX_ALL},
|
||||
{"asm", JANET_SANDBOX_ASM},
|
||||
{"chroot", JANET_SANDBOX_CHROOT},
|
||||
{"compile", JANET_SANDBOX_COMPILE},
|
||||
{"env", JANET_SANDBOX_ENV},
|
||||
{"ffi", JANET_SANDBOX_FFI},
|
||||
{"ffi-define", JANET_SANDBOX_FFI_DEFINE},
|
||||
@@ -770,8 +769,6 @@ static const SandboxOption sandbox_options[] = {
|
||||
{"sandbox", JANET_SANDBOX_SANDBOX},
|
||||
{"signal", JANET_SANDBOX_SIGNAL},
|
||||
{"subprocess", JANET_SANDBOX_SUBPROCESS},
|
||||
{"threads", JANET_SANDBOX_THREADS},
|
||||
{"unmarshal", JANET_SANDBOX_UNMARSHAL},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
@@ -780,9 +777,6 @@ JANET_CORE_FN(janet_core_sandbox,
|
||||
"Disable feature sets to prevent the interpreter from using certain system resources. "
|
||||
"Once a feature is disabled, there is no way to re-enable it. Capabilities can be:\n\n"
|
||||
"* :all - disallow all (except IO to stdout, stderr, and stdin)\n"
|
||||
"* :asm - disallow calling `asm` and `disasm` functions.\n"
|
||||
"* :chroot - disallow calling `os/posix-chroot`\n"
|
||||
"* :compile - disallow calling `compile`. This will disable a lot of functionality, such as `eval`.\n"
|
||||
"* :env - disallow reading and write env variables\n"
|
||||
"* :ffi - disallow FFI (recommended if disabling anything else)\n"
|
||||
"* :ffi-define - disallow loading new FFI modules and binding new functions\n"
|
||||
@@ -799,9 +793,7 @@ JANET_CORE_FN(janet_core_sandbox,
|
||||
"* :net-listen - disallow accepting inbound network connections\n"
|
||||
"* :sandbox - disallow calling this function\n"
|
||||
"* :signal - disallow adding or removing signal handlers\n"
|
||||
"* :subprocess - disallow running subprocesses\n"
|
||||
"* :threads - disallow spawning threads with `ev/thread`. Certain helper threads may still be spawned.\n"
|
||||
"* :unmarshal - disallow calling the unmarshal function.\n") {
|
||||
"* :subprocess - disallow running subprocesses") {
|
||||
uint32_t flags = 0;
|
||||
for (int32_t i = 0; i < argc; i++) {
|
||||
JanetKeyword kw = janet_getkeyword(argv, i);
|
||||
@@ -1009,11 +1001,12 @@ static void make_apply(JanetTable *env) {
|
||||
janet_quick_asm(env, JANET_FUN_APPLY | JANET_FUNCDEF_FLAG_VARARG,
|
||||
"apply", 1, 1, INT32_MAX, 6, apply_asm, sizeof(apply_asm),
|
||||
JDOC("(apply f & args)\n\n"
|
||||
"Applies a function f to a variable number of arguments. Each "
|
||||
"element in args is used as an argument to f, except the last "
|
||||
"element in args, which is expected to be an array or a tuple. "
|
||||
"Each element in this last argument is then also pushed as an "
|
||||
"argument to f."));
|
||||
"Applies a function to a variable number of arguments. Each element in args "
|
||||
"is used as an argument to f, except the last element in args, which is expected to "
|
||||
"be an array-like. Each element in this last argument is then also pushed as an argument to "
|
||||
"f. For example:\n\n"
|
||||
"\t(apply + 1000 (range 10))\n\n"
|
||||
"sums the first 10 integers and 1000."));
|
||||
}
|
||||
|
||||
static const uint32_t error_asm[] = {
|
||||
@@ -1166,82 +1159,82 @@ JanetTable *janet_core_env(JanetTable *replacements) {
|
||||
janet_quick_asm(env, JANET_FUN_CMP,
|
||||
"cmp", 2, 2, 2, 2, cmp_asm, sizeof(cmp_asm),
|
||||
JDOC("(cmp x y)\n\n"
|
||||
"Returns -1 if x is strictly less than y, 1 if y is strictly greater "
|
||||
"than x, and 0 otherwise. To return 0, x and y must be the exact same type."));
|
||||
"Returns -1 if x is strictly less than y, 1 if y is strictly greater "
|
||||
"than x, and 0 otherwise. To return 0, x and y must be the exact same type."));
|
||||
janet_quick_asm(env, JANET_FUN_NEXT,
|
||||
"next", 2, 1, 2, 2, next_asm, sizeof(next_asm),
|
||||
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 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."));
|
||||
"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 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 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."));
|
||||
"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"
|
||||
"Throws a debug signal that can be caught by a parent fiber and used to inspect "
|
||||
"the running state of the current fiber. Returns the value passed in by resume."));
|
||||
"Throws a debug signal that can be caught by a parent fiber and used to inspect "
|
||||
"the running state of the current fiber. Returns the value passed in by resume."));
|
||||
janet_quick_asm(env, JANET_FUN_ERROR,
|
||||
"error", 1, 1, 1, 1, error_asm, sizeof(error_asm),
|
||||
JDOC("(error e)\n\n"
|
||||
"Throws an error e that can be caught and handled by a parent fiber."));
|
||||
"Throws an error e that can be caught and handled by a parent fiber."));
|
||||
janet_quick_asm(env, JANET_FUN_YIELD,
|
||||
"yield", 1, 0, 1, 2, yield_asm, sizeof(yield_asm),
|
||||
JDOC("(yield &opt x)\n\n"
|
||||
"Yield a value to a parent fiber. When a fiber yields, its execution is paused until "
|
||||
"another thread resumes it. The fiber will then resume, and the last yield call will "
|
||||
"return the value that was passed to resume."));
|
||||
"Yield a value to a parent fiber. When a fiber yields, its execution is paused until "
|
||||
"another thread resumes it. The fiber will then resume, and the last yield call will "
|
||||
"return the value that was passed to resume."));
|
||||
janet_quick_asm(env, JANET_FUN_CANCEL,
|
||||
"cancel", 2, 2, 2, 2, cancel_asm, sizeof(cancel_asm),
|
||||
JDOC("(cancel fiber err)\n\n"
|
||||
"Resume a fiber but have it immediately raise an error. This lets a programmer unwind a pending fiber. "
|
||||
"Returns the same result as resume."));
|
||||
"Resume a fiber but have it immediately raise an error. This lets a programmer unwind a pending fiber. "
|
||||
"Returns the same result as resume."));
|
||||
janet_quick_asm(env, JANET_FUN_RESUME,
|
||||
"resume", 2, 1, 2, 2, resume_asm, sizeof(resume_asm),
|
||||
JDOC("(resume fiber &opt x)\n\n"
|
||||
"Resume a new or suspended fiber and optionally pass in a value to the fiber that "
|
||||
"will be returned to the last yield in the case of a pending fiber, or the argument to "
|
||||
"the dispatch function in the case of a new fiber. Returns either the return result of "
|
||||
"the fiber's dispatch function, or the value from the next yield call in fiber."));
|
||||
"Resume a new or suspended fiber and optionally pass in a value to the fiber that "
|
||||
"will be returned to the last yield in the case of a pending fiber, or the argument to "
|
||||
"the dispatch function in the case of a new fiber. Returns either the return result of "
|
||||
"the fiber's dispatch function, or the value from the next yield call in fiber."));
|
||||
janet_quick_asm(env, JANET_FUN_IN,
|
||||
"in", 3, 2, 3, 4, in_asm, sizeof(in_asm),
|
||||
JDOC("(in ds key &opt dflt)\n\n"
|
||||
"Get value in ds at key, works on associative data structures. Arrays, tuples, tables, structs, "
|
||||
"strings, symbols, and buffers are all associative and can be used. Arrays, tuples, strings, buffers, "
|
||||
"and symbols must use integer keys that are in bounds or an error is raised. Structs and tables can "
|
||||
"take any value as a key except nil and will return nil or dflt if not found."));
|
||||
"Get value in ds at key, works on associative data structures. Arrays, tuples, tables, structs, "
|
||||
"strings, symbols, and buffers are all associative and can be used. Arrays, tuples, strings, buffers, "
|
||||
"and symbols must use integer keys that are in bounds or an error is raised. Structs and tables can "
|
||||
"take any value as a key except nil and will return nil or dflt if not found."));
|
||||
janet_quick_asm(env, JANET_FUN_GET,
|
||||
"get", 3, 2, 3, 4, get_asm, sizeof(in_asm),
|
||||
JDOC("(get ds key &opt dflt)\n\n"
|
||||
"Get the value mapped to key in data structure ds, and return dflt or nil if not found. "
|
||||
"Similar to in, but will not throw an error if the key is invalid for the data structure "
|
||||
"unless the data structure is an abstract type. In that case, the abstract type getter may throw "
|
||||
"an error."));
|
||||
"Get the value mapped to key in data structure ds, and return dflt or nil if not found. "
|
||||
"Similar to in, but will not throw an error if the key is invalid for the data structure "
|
||||
"unless the data structure is an abstract type. In that case, the abstract type getter may throw "
|
||||
"an error."));
|
||||
janet_quick_asm(env, JANET_FUN_PUT,
|
||||
"put", 3, 3, 3, 3, put_asm, sizeof(put_asm),
|
||||
JDOC("(put ds key value)\n\n"
|
||||
"Associate a key with a value in any mutable associative data structure. Indexed data structures "
|
||||
"(arrays and buffers) only accept non-negative integer keys, and will expand if an out of bounds "
|
||||
"value is provided. In an array, extra space will be filled with nils, and in a buffer, extra "
|
||||
"space will be filled with 0 bytes. In a table, putting a key that is contained in the table prototype "
|
||||
"will hide the association defined by the prototype, but will not mutate the prototype table. Putting "
|
||||
"a value nil into a table will remove the key from the table. Returns the data structure ds."));
|
||||
"Associate a key with a value in any mutable associative data structure. Indexed data structures "
|
||||
"(arrays and buffers) only accept non-negative integer keys, and will expand if an out of bounds "
|
||||
"value is provided. In an array, extra space will be filled with nils, and in a buffer, extra "
|
||||
"space will be filled with 0 bytes. In a table, putting a key that is contained in the table prototype "
|
||||
"will hide the association defined by the prototype, but will not mutate the prototype table. Putting "
|
||||
"a value nil into a table will remove the key from the table. Returns the data structure ds."));
|
||||
janet_quick_asm(env, JANET_FUN_LENGTH,
|
||||
"length", 1, 1, 1, 1, length_asm, sizeof(length_asm),
|
||||
JDOC("(length ds)\n\n"
|
||||
"Returns the length or count of a data structure in constant time as an integer. For "
|
||||
"structs and tables, returns the number of key-value pairs in the data structure."));
|
||||
"Returns the length or count of a data structure in constant time as an integer. For "
|
||||
"structs and tables, returns the number of key-value pairs in the data structure."));
|
||||
janet_quick_asm(env, JANET_FUN_BNOT,
|
||||
"bnot", 1, 1, 1, 1, bnot_asm, sizeof(bnot_asm),
|
||||
JDOC("(bnot x)\n\nReturns the bit-wise inverse of integer x."));
|
||||
@@ -1250,74 +1243,74 @@ JanetTable *janet_core_env(JanetTable *replacements) {
|
||||
/* Variadic ops */
|
||||
templatize_varop(env, JANET_FUN_ADD, "+", 0, 0, JOP_ADD,
|
||||
JDOC("(+ & xs)\n\n"
|
||||
"Returns the sum of all xs. If xs is empty, return 0."));
|
||||
"Returns the sum of all xs. xs must be integers or real numbers only. If xs is empty, return 0."));
|
||||
templatize_varop(env, JANET_FUN_SUBTRACT, "-", 0, 0, JOP_SUBTRACT,
|
||||
JDOC("(- & xs)\n\n"
|
||||
"Returns the difference of xs. If xs is empty, returns 0. If xs has one element, returns the "
|
||||
"negative value of that element. Otherwise, returns the first element in xs minus the sum of "
|
||||
"the rest of the elements."));
|
||||
"Returns the difference of xs. If xs is empty, returns 0. If xs has one element, returns the "
|
||||
"negative value of that element. Otherwise, returns the first element in xs minus the sum of "
|
||||
"the rest of the elements."));
|
||||
templatize_varop(env, JANET_FUN_MULTIPLY, "*", 1, 1, JOP_MULTIPLY,
|
||||
JDOC("(* & xs)\n\n"
|
||||
"Returns the product of all elements in xs. If xs is empty, returns 1."));
|
||||
"Returns the product of all elements in xs. If xs is empty, returns 1."));
|
||||
templatize_varop(env, JANET_FUN_DIVIDE, "/", 1, 1, JOP_DIVIDE,
|
||||
JDOC("(/ & xs)\n\n"
|
||||
"Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
"Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
templatize_varop(env, JANET_FUN_DIVIDE_FLOOR, "div", 1, 1, JOP_DIVIDE_FLOOR,
|
||||
JDOC("(div & xs)\n\n"
|
||||
"Returns the floored division of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
"Returns the floored division of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
templatize_varop(env, JANET_FUN_MODULO, "mod", 0, 1, JOP_MODULO,
|
||||
JDOC("(mod & xs)\n\n"
|
||||
"Returns the result of applying the modulo operator on the first value of xs with each remaining value. "
|
||||
"`(mod x 0)` is defined to be `x`."));
|
||||
"Returns the result of applying the modulo operator on the first value of xs with each remaining value. "
|
||||
"`(mod x 0)` is defined to be `x`."));
|
||||
templatize_varop(env, JANET_FUN_REMAINDER, "%", 0, 1, JOP_REMAINDER,
|
||||
JDOC("(% & xs)\n\n"
|
||||
"Returns the remainder of dividing the first value of xs by each remaining value."));
|
||||
"Returns the remainder of dividing the first value of xs by each remaining value."));
|
||||
templatize_varop(env, JANET_FUN_BAND, "band", -1, -1, JOP_BAND,
|
||||
JDOC("(band & xs)\n\n"
|
||||
"Returns the bit-wise and of all values in xs. Each x in xs must be an integer."));
|
||||
"Returns the bit-wise and of all values in xs. Each x in xs must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_BOR, "bor", 0, 0, JOP_BOR,
|
||||
JDOC("(bor & xs)\n\n"
|
||||
"Returns the bit-wise or of all values in xs. Each x in xs must be an integer."));
|
||||
"Returns the bit-wise or of all values in xs. Each x in xs must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_BXOR, "bxor", 0, 0, JOP_BXOR,
|
||||
JDOC("(bxor & xs)\n\n"
|
||||
"Returns the bit-wise xor of all values in xs. Each x in xs must be an integer."));
|
||||
"Returns the bit-wise xor of all values in xs. Each in xs must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_LSHIFT, "blshift", 1, 1, JOP_SHIFT_LEFT,
|
||||
JDOC("(blshift x & shifts)\n\n"
|
||||
"Returns the value of x bit shifted left by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer."));
|
||||
"Returns the value of x bit shifted left by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_RSHIFT, "brshift", 1, 1, JOP_SHIFT_RIGHT,
|
||||
JDOC("(brshift x & shifts)\n\n"
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer."));
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_RSHIFTU, "brushift", 1, 1, JOP_SHIFT_RIGHT_UNSIGNED,
|
||||
JDOC("(brushift x & shifts)\n\n"
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer. The sign of x is not preserved, so "
|
||||
"for positive shifts the return value will always be positive."));
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer. The sign of x is not preserved, so "
|
||||
"for positive shifts the return value will always be positive."));
|
||||
|
||||
/* Variadic comparators */
|
||||
templatize_comparator(env, JANET_FUN_GT, ">", 0, JOP_GREATER_THAN,
|
||||
JDOC("(> & xs)\n\n"
|
||||
"Check if xs is in descending order. Returns a boolean."));
|
||||
"Check if xs is in descending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_LT, "<", 0, JOP_LESS_THAN,
|
||||
JDOC("(< & xs)\n\n"
|
||||
"Check if xs is in ascending order. Returns a boolean."));
|
||||
"Check if xs is in ascending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_GTE, ">=", 0, JOP_GREATER_THAN_EQUAL,
|
||||
JDOC("(>= & xs)\n\n"
|
||||
"Check if xs is in non-ascending order. Returns a boolean."));
|
||||
"Check if xs is in non-ascending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_LTE, "<=", 0, JOP_LESS_THAN_EQUAL,
|
||||
JDOC("(<= & xs)\n\n"
|
||||
"Check if xs is in non-descending order. Returns a boolean."));
|
||||
"Check if xs is in non-descending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_EQ, "=", 0, JOP_EQUALS,
|
||||
JDOC("(= & xs)\n\n"
|
||||
"Check if all values in xs are equal. Returns a boolean."));
|
||||
"Check if all values in xs are equal. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_NEQ, "not=", 1, JOP_EQUALS,
|
||||
JDOC("(not= & xs)\n\n"
|
||||
"Check if any values in xs are not equal. Returns a boolean."));
|
||||
"Check if any values in xs are not equal. Returns a boolean."));
|
||||
|
||||
/* Platform detection */
|
||||
janet_def(env, "janet/version", janet_cstringv(JANET_VERSION),
|
||||
@@ -1326,7 +1319,7 @@ JanetTable *janet_core_env(JanetTable *replacements) {
|
||||
JDOC("The build identifier of the running janet program."));
|
||||
janet_def(env, "janet/config-bits", janet_wrap_integer(JANET_CURRENT_CONFIG_BITS),
|
||||
JDOC("The flag set of config options from janetconf.h which is used to check "
|
||||
"if native modules are compatible with the host program."));
|
||||
"if native modules are compatible with the host program."));
|
||||
|
||||
/* Allow references to the environment */
|
||||
janet_def(env, "root-env", janet_wrap_table(env),
|
||||
@@ -1366,16 +1359,12 @@ JanetTable *janet_core_env(JanetTable *replacements) {
|
||||
lidv = midv = janet_wrap_nil();
|
||||
janet_resolve(env, janet_csymbol("load-image-dict"), &lidv);
|
||||
janet_resolve(env, janet_csymbol("make-image-dict"), &midv);
|
||||
|
||||
/* Check that we actually got tables - if we are using a smaller corelib, may not exist */
|
||||
if (janet_checktype(lidv, JANET_TABLE) && janet_checktype(midv, JANET_TABLE)) {
|
||||
JanetTable *lid = janet_unwrap_table(lidv);
|
||||
JanetTable *mid = janet_unwrap_table(midv);
|
||||
for (int32_t i = 0; i < lid->capacity; i++) {
|
||||
const JanetKV *kv = lid->data + i;
|
||||
if (!janet_checktype(kv->key, JANET_NIL)) {
|
||||
janet_table_put(mid, kv->value, kv->key);
|
||||
}
|
||||
JanetTable *lid = janet_unwrap_table(lidv);
|
||||
JanetTable *mid = janet_unwrap_table(midv);
|
||||
for (int32_t i = 0; i < lid->capacity; i++) {
|
||||
const JanetKV *kv = lid->data + i;
|
||||
if (!janet_checktype(kv->key, JANET_NIL)) {
|
||||
janet_table_put(mid, kv->value, kv->key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
402
src/core/ev.c
402
src/core/ev.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -112,16 +112,6 @@ typedef struct {
|
||||
JanetHandle write_pipe;
|
||||
} JanetEVThreadInit;
|
||||
|
||||
/* Structure used to initialize threads that run timeouts */
|
||||
typedef struct {
|
||||
double sec;
|
||||
JanetVM *vm;
|
||||
JanetFiber *fiber;
|
||||
#ifdef JANET_WINDOWS
|
||||
HANDLE cancel_event;
|
||||
#endif
|
||||
} JanetThreadedTimeout;
|
||||
|
||||
#define JANET_MAX_Q_CAPACITY 0x7FFFFFF
|
||||
|
||||
static void janet_q_init(JanetQueue *q) {
|
||||
@@ -356,22 +346,21 @@ JanetStream *janet_stream(JanetHandle handle, uint32_t flags, const JanetMethod
|
||||
|
||||
static void janet_stream_close_impl(JanetStream *stream) {
|
||||
stream->flags |= JANET_STREAM_CLOSED;
|
||||
int canclose = !(stream->flags & JANET_STREAM_NOT_CLOSEABLE);
|
||||
#ifdef JANET_WINDOWS
|
||||
if (stream->handle != INVALID_HANDLE_VALUE) {
|
||||
#ifdef JANET_NET
|
||||
if (stream->flags & JANET_STREAM_SOCKET) {
|
||||
if (canclose) closesocket((SOCKET) stream->handle);
|
||||
closesocket((SOCKET) stream->handle);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (canclose) CloseHandle(stream->handle);
|
||||
CloseHandle(stream->handle);
|
||||
}
|
||||
stream->handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
#else
|
||||
if (stream->handle != -1) {
|
||||
if (canclose) close(stream->handle);
|
||||
close(stream->handle);
|
||||
stream->handle = -1;
|
||||
#ifdef JANET_EV_POLL
|
||||
uint32_t i = stream->index;
|
||||
@@ -524,9 +513,9 @@ static void janet_schedule_general(JanetFiber *fiber, Janet value, JanetSignal s
|
||||
fiber->gc.flags |= JANET_FIBER_FLAG_ROOT;
|
||||
if (sig == JANET_SIGNAL_ERROR) fiber->gc.flags |= JANET_FIBER_EV_FLAG_CANCELED;
|
||||
if (soon) {
|
||||
janet_assert(!janet_q_push_head(&janet_vm.spawn, &t, sizeof(t)), "schedule queue overflow");
|
||||
janet_q_push_head(&janet_vm.spawn, &t, sizeof(t));
|
||||
} else {
|
||||
janet_assert(!janet_q_push(&janet_vm.spawn, &t, sizeof(t)), "schedule queue overflow");
|
||||
janet_q_push(&janet_vm.spawn, &t, sizeof(t));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,9 +528,6 @@ void janet_schedule_soon(JanetFiber *fiber, Janet value, JanetSignal sig) {
|
||||
}
|
||||
|
||||
void janet_cancel(JanetFiber *fiber, Janet value) {
|
||||
if (!(fiber->gc.flags & JANET_FIBER_FLAG_ROOT)) {
|
||||
janet_panic("cannot cancel non-task fiber");
|
||||
}
|
||||
janet_schedule_signal(fiber, value, JANET_SIGNAL_ERROR);
|
||||
}
|
||||
|
||||
@@ -610,43 +596,8 @@ void janet_ev_init_common(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if JANET_ANDROID
|
||||
static void janet_timeout_stop(int sig_num) {
|
||||
if (sig_num == SIGUSR1) {
|
||||
pthread_exit(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void handle_timeout_worker(JanetTimeout to, int cancel) {
|
||||
if (!to.has_worker) return;
|
||||
#ifdef JANET_WINDOWS
|
||||
if (cancel && to.worker_event) {
|
||||
SetEvent(to.worker_event);
|
||||
}
|
||||
WaitForSingleObject(to.worker, INFINITE);
|
||||
CloseHandle(to.worker);
|
||||
if (to.worker_event) {
|
||||
CloseHandle(to.worker_event);
|
||||
}
|
||||
#else
|
||||
#ifdef JANET_ANDROID
|
||||
if (cancel) janet_assert(!pthread_kill(to.worker, SIGUSR1), "pthread_kill");
|
||||
#else
|
||||
if (cancel) janet_assert(!pthread_cancel(to.worker), "pthread_cancel");
|
||||
#endif
|
||||
void *res = NULL;
|
||||
janet_assert(!pthread_join(to.worker, &res), "pthread_join");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Common deinit code */
|
||||
void janet_ev_deinit_common(void) {
|
||||
JanetTimeout to;
|
||||
while (peek_timeout(&to)) {
|
||||
handle_timeout_worker(to, 1);
|
||||
pop_timeout(0);
|
||||
}
|
||||
janet_q_deinit(&janet_vm.spawn);
|
||||
janet_free(janet_vm.tq);
|
||||
janet_table_deinit(&janet_vm.threaded_abstracts);
|
||||
@@ -672,7 +623,6 @@ void janet_addtimeout(double sec) {
|
||||
to.curr_fiber = NULL;
|
||||
to.sched_id = fiber->sched_id;
|
||||
to.is_error = 1;
|
||||
to.has_worker = 0;
|
||||
add_timeout(to);
|
||||
}
|
||||
|
||||
@@ -685,61 +635,9 @@ void janet_addtimeout_nil(double sec) {
|
||||
to.curr_fiber = NULL;
|
||||
to.sched_id = fiber->sched_id;
|
||||
to.is_error = 0;
|
||||
to.has_worker = 0;
|
||||
add_timeout(to);
|
||||
}
|
||||
|
||||
static void janet_timeout_cb(JanetEVGenericMessage msg) {
|
||||
(void) msg;
|
||||
janet_interpreter_interrupt_handled(&janet_vm);
|
||||
}
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
static DWORD WINAPI janet_timeout_body(LPVOID ptr) {
|
||||
JanetThreadedTimeout tto = *(JanetThreadedTimeout *)ptr;
|
||||
janet_free(ptr);
|
||||
JanetTimestamp wait_begin = ts_now();
|
||||
DWORD duration = (DWORD)round(tto.sec * 1000);
|
||||
DWORD res = WAIT_TIMEOUT;
|
||||
JanetTimestamp wait_end = ts_now();
|
||||
for (DWORD i = 1; res == WAIT_TIMEOUT && (wait_end - wait_begin) < duration; i++) {
|
||||
res = WaitForSingleObject(tto.cancel_event, (duration + i));
|
||||
wait_end = ts_now();
|
||||
}
|
||||
/* only send interrupt message if result is WAIT_TIMEOUT */
|
||||
if (res == WAIT_TIMEOUT) {
|
||||
janet_interpreter_interrupt(tto.vm);
|
||||
JanetEVGenericMessage msg = {0};
|
||||
janet_ev_post_event(tto.vm, janet_timeout_cb, msg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static void *janet_timeout_body(void *ptr) {
|
||||
#ifdef JANET_ANDROID
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
action.sa_handler = &janet_timeout_stop;
|
||||
sigaction(SIGUSR1, &action, NULL);
|
||||
#endif
|
||||
JanetThreadedTimeout tto = *(JanetThreadedTimeout *)ptr;
|
||||
janet_free(ptr);
|
||||
struct timespec ts;
|
||||
ts.tv_sec = (time_t) tto.sec;
|
||||
ts.tv_nsec = (tto.sec <= UINT32_MAX)
|
||||
? (long)((tto.sec - ((uint32_t)tto.sec)) * 1000000000)
|
||||
: 0;
|
||||
nanosleep(&ts, &ts);
|
||||
janet_interpreter_interrupt(tto.vm);
|
||||
JanetEVGenericMessage msg = {0};
|
||||
janet_ev_post_event(tto.vm, janet_timeout_cb, msg);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void janet_ev_inc_refcount(void) {
|
||||
janet_atomic_inc(&janet_vm.listener_count);
|
||||
}
|
||||
@@ -854,34 +752,6 @@ static int janet_chanat_gc(void *p, size_t s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void janet_chanat_remove_vmref(JanetQueue *fq) {
|
||||
JanetChannelPending *pending = fq->data;
|
||||
if (fq->head <= fq->tail) {
|
||||
for (int32_t i = fq->head; i < fq->tail; i++) {
|
||||
if (pending[i].thread == &janet_vm) pending[i].thread = NULL;
|
||||
}
|
||||
} else {
|
||||
for (int32_t i = fq->head; i < fq->capacity; i++) {
|
||||
if (pending[i].thread == &janet_vm) pending[i].thread = NULL;
|
||||
}
|
||||
for (int32_t i = 0; i < fq->tail; i++) {
|
||||
if (pending[i].thread == &janet_vm) pending[i].thread = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int janet_chanat_gcperthread(void *p, size_t s) {
|
||||
(void) s;
|
||||
JanetChannel *chan = p;
|
||||
janet_chan_lock(chan);
|
||||
/* Make sure that the internals of the threaded channel no longer reference _this_ thread. Replace
|
||||
* those references with NULL. */
|
||||
janet_chanat_remove_vmref(&chan->read_pending);
|
||||
janet_chanat_remove_vmref(&chan->write_pending);
|
||||
janet_chan_unlock(chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void janet_chanat_mark_fq(JanetQueue *fq) {
|
||||
JanetChannelPending *pending = fq->data;
|
||||
if (fq->head <= fq->tail) {
|
||||
@@ -959,15 +829,13 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
}
|
||||
} else if (mode != JANET_CP_MODE_CLOSE) {
|
||||
/* Fiber has already been canceled or resumed. */
|
||||
/* Fiber has already been cancelled or resumed. */
|
||||
/* Resend event to another waiting thread, depending on mode */
|
||||
int is_read = (mode == JANET_CP_MODE_CHOICE_READ) || (mode == JANET_CP_MODE_READ);
|
||||
if (is_read) {
|
||||
JanetChannelPending reader;
|
||||
int sent = 0;
|
||||
while (!janet_q_pop(&channel->read_pending, &reader, sizeof(reader))) {
|
||||
if (!janet_q_pop(&channel->read_pending, &reader, sizeof(reader))) {
|
||||
JanetVM *vm = reader.thread;
|
||||
if (!vm) continue;
|
||||
JanetEVGenericMessage msg;
|
||||
msg.tag = reader.mode;
|
||||
msg.fiber = reader.fiber;
|
||||
@@ -975,17 +843,11 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
|
||||
msg.argp = channel;
|
||||
msg.argj = x;
|
||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||
sent = 1;
|
||||
break;
|
||||
}
|
||||
if (!sent) {
|
||||
janet_chan_unpack(channel, &x, 1);
|
||||
}
|
||||
} else {
|
||||
JanetChannelPending writer;
|
||||
while (!janet_q_pop(&channel->write_pending, &writer, sizeof(writer))) {
|
||||
if (!janet_q_pop(&channel->write_pending, &writer, sizeof(writer))) {
|
||||
JanetVM *vm = writer.thread;
|
||||
if (!vm) continue;
|
||||
JanetEVGenericMessage msg;
|
||||
msg.tag = writer.mode;
|
||||
msg.fiber = writer.fiber;
|
||||
@@ -993,7 +855,6 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
|
||||
msg.argp = channel;
|
||||
msg.argj = janet_wrap_nil();
|
||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1006,14 +867,14 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
|
||||
static int janet_channel_push_with_lock(JanetChannel *channel, Janet x, int mode) {
|
||||
JanetChannelPending reader;
|
||||
int is_empty;
|
||||
if (channel->closed) {
|
||||
janet_chan_unlock(channel);
|
||||
janet_panic("cannot write to closed channel");
|
||||
}
|
||||
if (janet_chan_pack(channel, &x)) {
|
||||
janet_chan_unlock(channel);
|
||||
janet_panicf("failed to pack value for channel: %v", x);
|
||||
}
|
||||
if (channel->closed) {
|
||||
janet_chan_unlock(channel);
|
||||
janet_panic("cannot write to closed channel");
|
||||
}
|
||||
int is_threaded = janet_chan_is_threaded(channel);
|
||||
if (is_threaded) {
|
||||
/* don't dereference fiber from another thread */
|
||||
@@ -1026,7 +887,6 @@ static int janet_channel_push_with_lock(JanetChannel *channel, Janet x, int mode
|
||||
if (is_empty) {
|
||||
/* No pending reader */
|
||||
if (janet_q_push(&channel->items, &x, sizeof(Janet))) {
|
||||
janet_chan_unpack(channel, &x, 1);
|
||||
janet_chan_unlock(channel);
|
||||
janet_panicf("channel overflow: %v", x);
|
||||
} else if (janet_q_count(&channel->items) > channel->limit) {
|
||||
@@ -1058,12 +918,7 @@ static int janet_channel_push_with_lock(JanetChannel *channel, Janet x, int mode
|
||||
msg.argi = (int32_t) reader.sched_id;
|
||||
msg.argp = channel;
|
||||
msg.argj = x;
|
||||
if (vm) {
|
||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||
} else {
|
||||
/* If no vm to send to, we must clean up (unpack) the packed payload to avoid leak */
|
||||
janet_chan_unpack(channel, &x, 1);
|
||||
}
|
||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||
} else {
|
||||
if (reader.mode == JANET_CP_MODE_CHOICE_READ) {
|
||||
janet_schedule(reader.fiber, make_read_result(channel, x));
|
||||
@@ -1094,7 +949,7 @@ static int janet_channel_pop_with_lock(JanetChannel *channel, Janet *item, int i
|
||||
int is_threaded = janet_chan_is_threaded(channel);
|
||||
if (janet_q_pop(&channel->items, item, sizeof(Janet))) {
|
||||
/* Queue empty */
|
||||
if (is_choice == 2) return 0; /* Skip pending read */
|
||||
if (is_choice == 2) return 0; // Skip pending read
|
||||
JanetChannelPending pending;
|
||||
pending.thread = &janet_vm;
|
||||
pending.fiber = janet_vm.root_fiber,
|
||||
@@ -1118,9 +973,7 @@ static int janet_channel_pop_with_lock(JanetChannel *channel, Janet *item, int i
|
||||
msg.argi = (int32_t) writer.sched_id;
|
||||
msg.argp = channel;
|
||||
msg.argj = janet_wrap_nil();
|
||||
if (vm) {
|
||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||
}
|
||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||
} else {
|
||||
if (writer.mode == JANET_CP_MODE_CHOICE_WRITE) {
|
||||
janet_schedule(writer.fiber, make_write_result(channel));
|
||||
@@ -1172,7 +1025,7 @@ JanetChannel *janet_channel_make(uint32_t limit) {
|
||||
JanetChannel *janet_channel_make_threaded(uint32_t limit) {
|
||||
janet_assert(limit <= INT32_MAX, "bad limit");
|
||||
JanetChannel *channel = janet_abstract_threaded(&janet_channel_type, sizeof(JanetChannel));
|
||||
janet_chan_init(channel, (int32_t) limit, 1);
|
||||
janet_chan_init(channel, (int32_t) limit, 0);
|
||||
return channel;
|
||||
}
|
||||
|
||||
@@ -1184,9 +1037,6 @@ JANET_CORE_FN(cfun_channel_push,
|
||||
"Returns the channel if the write succeeded, nil otherwise.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetChannel *channel = janet_getchannel(argv, 0);
|
||||
if (janet_vm.coerce_error) {
|
||||
janet_panic("cannot give to channel inside janet_call");
|
||||
}
|
||||
if (janet_channel_push(channel, argv[1], 0)) {
|
||||
janet_await();
|
||||
}
|
||||
@@ -1199,15 +1049,26 @@ JANET_CORE_FN(cfun_channel_pop,
|
||||
janet_fixarity(argc, 1);
|
||||
JanetChannel *channel = janet_getchannel(argv, 0);
|
||||
Janet item;
|
||||
if (janet_vm.coerce_error) {
|
||||
janet_panic("cannot take from channel inside janet_call");
|
||||
}
|
||||
if (janet_channel_pop(channel, &item, 0)) {
|
||||
janet_schedule(janet_vm.root_fiber, item);
|
||||
}
|
||||
janet_await();
|
||||
}
|
||||
|
||||
static void chan_unlock_args(const Janet *argv, int32_t n) {
|
||||
for (int32_t i = 0; i < n; i++) {
|
||||
int32_t len;
|
||||
const Janet *data;
|
||||
JanetChannel *chan;
|
||||
if (janet_indexed_view(argv[i], &data, &len) && len == 2) {
|
||||
chan = janet_getchannel(data, 0);
|
||||
} else {
|
||||
chan = janet_getchannel(argv, i);
|
||||
}
|
||||
janet_chan_unlock(chan);
|
||||
}
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_channel_choice,
|
||||
"(ev/select & clauses)",
|
||||
"Block until the first of several channel operations occur. Returns a "
|
||||
@@ -1224,10 +1085,6 @@ JANET_CORE_FN(cfun_channel_choice,
|
||||
int32_t len;
|
||||
const Janet *data;
|
||||
|
||||
if (janet_vm.coerce_error) {
|
||||
janet_panic("cannot select from channel inside janet_call");
|
||||
}
|
||||
|
||||
/* Check channels for immediate reads and writes */
|
||||
for (int32_t i = 0; i < argc; i++) {
|
||||
if (janet_indexed_view(argv[i], &data, &len) && len == 2) {
|
||||
@@ -1236,27 +1093,29 @@ JANET_CORE_FN(cfun_channel_choice,
|
||||
janet_chan_lock(chan);
|
||||
if (chan->closed) {
|
||||
janet_chan_unlock(chan);
|
||||
chan_unlock_args(argv, i);
|
||||
return make_close_result(chan);
|
||||
}
|
||||
if (janet_q_count(&chan->items) < chan->limit) {
|
||||
janet_channel_push_with_lock(chan, data[1], 1);
|
||||
chan_unlock_args(argv, i);
|
||||
return make_write_result(chan);
|
||||
}
|
||||
janet_chan_unlock(chan);
|
||||
} else {
|
||||
/* Read */
|
||||
JanetChannel *chan = janet_getchannel(argv, i);
|
||||
janet_chan_lock(chan);
|
||||
if (chan->closed) {
|
||||
janet_chan_unlock(chan);
|
||||
chan_unlock_args(argv, i);
|
||||
return make_close_result(chan);
|
||||
}
|
||||
if (chan->items.head != chan->items.tail) {
|
||||
Janet item;
|
||||
janet_channel_pop_with_lock(chan, &item, 1);
|
||||
chan_unlock_args(argv, i);
|
||||
return make_read_result(chan, item);
|
||||
}
|
||||
janet_chan_unlock(chan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1265,13 +1124,11 @@ JANET_CORE_FN(cfun_channel_choice,
|
||||
if (janet_indexed_view(argv[i], &data, &len) && len == 2) {
|
||||
/* Write */
|
||||
JanetChannel *chan = janet_getchannel(data, 0);
|
||||
janet_chan_lock(chan);
|
||||
janet_channel_push_with_lock(chan, data[1], 1);
|
||||
} else {
|
||||
/* Read */
|
||||
Janet item;
|
||||
JanetChannel *chan = janet_getchannel(argv, i);
|
||||
janet_chan_lock(chan);
|
||||
janet_channel_pop_with_lock(chan, &item, 1);
|
||||
}
|
||||
}
|
||||
@@ -1370,11 +1227,9 @@ JANET_CORE_FN(cfun_channel_close,
|
||||
msg.tag = JANET_CP_MODE_CLOSE;
|
||||
msg.argi = (int32_t) writer.sched_id;
|
||||
msg.argj = janet_wrap_nil();
|
||||
if (vm) {
|
||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||
}
|
||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||
} else {
|
||||
if (janet_fiber_can_resume(writer.fiber) && writer.sched_id == writer.fiber->sched_id) {
|
||||
if (janet_fiber_can_resume(writer.fiber)) {
|
||||
if (writer.mode == JANET_CP_MODE_CHOICE_WRITE) {
|
||||
janet_schedule(writer.fiber, make_close_result(channel));
|
||||
} else {
|
||||
@@ -1393,11 +1248,9 @@ JANET_CORE_FN(cfun_channel_close,
|
||||
msg.tag = JANET_CP_MODE_CLOSE;
|
||||
msg.argi = (int32_t) reader.sched_id;
|
||||
msg.argj = janet_wrap_nil();
|
||||
if (vm) {
|
||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||
}
|
||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||
} else {
|
||||
if (janet_fiber_can_resume(reader.fiber) && reader.sched_id == reader.fiber->sched_id) {
|
||||
if (janet_fiber_can_resume(reader.fiber)) {
|
||||
if (reader.mode == JANET_CP_MODE_CHOICE_READ) {
|
||||
janet_schedule(reader.fiber, make_close_result(channel));
|
||||
} else {
|
||||
@@ -1467,12 +1320,11 @@ static void *janet_chanat_unmarshal(JanetMarshalContext *ctx) {
|
||||
int32_t limit = janet_unmarshal_int(ctx);
|
||||
int32_t count = janet_unmarshal_int(ctx);
|
||||
if (count < 0) janet_panic("invalid negative channel count");
|
||||
if (count > limit) janet_panic("invalid channel count");
|
||||
janet_chan_init(abst, limit, 0);
|
||||
abst->closed = !!is_closed;
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
Janet item = janet_unmarshal_janet(ctx);
|
||||
janet_assert(!janet_q_push(&abst->items, &item, sizeof(item)), "bad unmarshal channel");
|
||||
janet_q_push(&abst->items, &item, sizeof(item));
|
||||
}
|
||||
return abst;
|
||||
}
|
||||
@@ -1489,10 +1341,7 @@ const JanetAbstractType janet_channel_type = {
|
||||
NULL, /* compare */
|
||||
NULL, /* hash */
|
||||
janet_chanat_next,
|
||||
NULL, /* call */
|
||||
NULL, /* length */
|
||||
NULL, /* bytes */
|
||||
janet_chanat_gcperthread
|
||||
JANET_ATEND_NEXT
|
||||
};
|
||||
|
||||
/* Main event loop */
|
||||
@@ -1525,13 +1374,12 @@ JanetFiber *janet_loop1(void) {
|
||||
}
|
||||
}
|
||||
}
|
||||
handle_timeout_worker(to, 0);
|
||||
}
|
||||
|
||||
/* Run scheduled fibers unless interrupts need to be handled. */
|
||||
while (janet_vm.spawn.head != janet_vm.spawn.tail) {
|
||||
/* Don't run until all interrupts have been marked as handled by calling janet_interpreter_interrupt_handled */
|
||||
if (janet_atomic_load_relaxed(&janet_vm.auto_suspend)) break;
|
||||
if (janet_vm.auto_suspend) break;
|
||||
JanetTask task = {NULL, janet_wrap_nil(), JANET_SIGNAL_OK, 0};
|
||||
janet_q_pop(&janet_vm.spawn, &task, sizeof(task));
|
||||
if (task.fiber->gc.flags & JANET_FIBER_EV_FLAG_SUSPENDED) janet_ev_dec_refcount();
|
||||
@@ -1573,14 +1421,12 @@ JanetFiber *janet_loop1(void) {
|
||||
while ((has_timeout = peek_timeout(&to))) {
|
||||
if (to.curr_fiber != NULL) {
|
||||
if (!janet_fiber_can_resume(to.curr_fiber)) {
|
||||
pop_timeout(0);
|
||||
janet_table_remove(&janet_vm.active_tasks, janet_wrap_fiber(to.curr_fiber));
|
||||
handle_timeout_worker(to, 1);
|
||||
pop_timeout(0);
|
||||
continue;
|
||||
}
|
||||
} else if (to.fiber->sched_id != to.sched_id) {
|
||||
pop_timeout(0);
|
||||
handle_timeout_worker(to, 1);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
@@ -1712,20 +1558,20 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp to) {
|
||||
janet_free(response);
|
||||
} else {
|
||||
/* Normal event */
|
||||
JanetOverlapped *jo = (JanetOverlapped *) overlapped;
|
||||
JanetStream *stream = (JanetStream *) completionKey;
|
||||
JanetFiber *fiber = NULL;
|
||||
if (stream->read_fiber && stream->read_fiber->ev_state == jo) {
|
||||
if (stream->read_fiber && stream->read_fiber->ev_state == overlapped) {
|
||||
fiber = stream->read_fiber;
|
||||
} else if (stream->write_fiber && stream->write_fiber->ev_state == jo) {
|
||||
} else if (stream->write_fiber && stream->write_fiber->ev_state == overlapped) {
|
||||
fiber = stream->write_fiber;
|
||||
}
|
||||
if (fiber != NULL) {
|
||||
fiber->flags &= ~JANET_FIBER_EV_FLAG_IN_FLIGHT;
|
||||
jo->bytes_transfered = (ULONG_PTR) num_bytes_transferred;
|
||||
/* System is done with this, we can reused this data */
|
||||
overlapped->InternalHigh = (ULONG_PTR) num_bytes_transferred;
|
||||
fiber->ev_callback(fiber, result ? JANET_ASYNC_EVENT_COMPLETE : JANET_ASYNC_EVENT_FAILED);
|
||||
} else {
|
||||
janet_free((void *) jo);
|
||||
janet_free((void *) overlapped);
|
||||
janet_ev_dec_refcount();
|
||||
}
|
||||
janet_stream_checktoclose(stream);
|
||||
@@ -1745,7 +1591,7 @@ void janet_stream_level_triggered(JanetStream *stream) {
|
||||
|
||||
static JanetTimestamp ts_now(void) {
|
||||
struct timespec now;
|
||||
janet_assert(-1 != janet_gettime(&now, JANET_TIME_MONOTONIC), "failed to get time");
|
||||
janet_assert(-1 != clock_gettime(CLOCK_MONOTONIC, &now), "failed to get time");
|
||||
uint64_t res = 1000 * now.tv_sec;
|
||||
res += now.tv_nsec / 1000000;
|
||||
return res;
|
||||
@@ -1903,7 +1749,7 @@ JanetTimestamp to_interval(const JanetTimestamp ts) {
|
||||
|
||||
static JanetTimestamp ts_now(void) {
|
||||
struct timespec now;
|
||||
janet_assert(-1 != janet_gettime(&now, JANET_TIME_MONOTONIC), "failed to get time");
|
||||
janet_assert(-1 != clock_gettime(CLOCK_MONOTONIC, &now), "failed to get time");
|
||||
uint64_t res = 1000 * now.tv_sec;
|
||||
res += now.tv_nsec / 1000000;
|
||||
return res;
|
||||
@@ -1943,22 +1789,6 @@ void janet_stream_edge_triggered(JanetStream *stream) {
|
||||
}
|
||||
|
||||
void janet_stream_level_triggered(JanetStream *stream) {
|
||||
/* On macos, we seem to need to delete any registered events before re-registering without
|
||||
* EV_CLEAR, otherwise the new event will still have EV_CLEAR set erroneously. This could be a
|
||||
* kernel bug, but unfortunately the specification is vague here, esp. in regards to where and when
|
||||
* EV_CLEAR is set automatically. */
|
||||
struct kevent kevs[2];
|
||||
int length = 0;
|
||||
if (stream->flags & (JANET_STREAM_READABLE | JANET_STREAM_ACCEPTABLE)) {
|
||||
EV_SETx(&kevs[length++], stream->handle, EVFILT_READ, EV_DELETE, 0, 0, stream);
|
||||
}
|
||||
if (stream->flags & JANET_STREAM_WRITABLE) {
|
||||
EV_SETx(&kevs[length++], stream->handle, EVFILT_WRITE, EV_DELETE, 0, 0, stream);
|
||||
}
|
||||
int status;
|
||||
do {
|
||||
status = kevent(janet_vm.kq, kevs, length, NULL, 0, NULL);
|
||||
} while (status == -1 && errno == EINTR);
|
||||
janet_register_stream_impl(stream, 0);
|
||||
}
|
||||
|
||||
@@ -2057,7 +1887,7 @@ void janet_ev_deinit(void) {
|
||||
|
||||
static JanetTimestamp ts_now(void) {
|
||||
struct timespec now;
|
||||
janet_assert(-1 != janet_gettime(&now, JANET_TIME_MONOTONIC), "failed to get time");
|
||||
janet_assert(-1 != clock_gettime(CLOCK_REALTIME, &now), "failed to get time");
|
||||
uint64_t res = 1000 * now.tv_sec;
|
||||
res += now.tv_nsec / 1000000;
|
||||
return res;
|
||||
@@ -2229,7 +2059,7 @@ void janet_ev_post_event(JanetVM *vm, JanetCallback cb, JanetEVGenericMessage ms
|
||||
event.cb = cb;
|
||||
int fd = vm->selfpipe[1];
|
||||
/* handle a bit of back pressure before giving up. */
|
||||
int tries = 20;
|
||||
int tries = 4;
|
||||
while (tries > 0) {
|
||||
int status;
|
||||
do {
|
||||
@@ -2410,7 +2240,7 @@ Janet janet_ev_lasterr(void) {
|
||||
msgbuf,
|
||||
sizeof(msgbuf),
|
||||
NULL);
|
||||
if (!*msgbuf) snprintf(msgbuf, sizeof(msgbuf), "%d", code);
|
||||
if (!*msgbuf) sprintf(msgbuf, "%d", code);
|
||||
char *c = msgbuf;
|
||||
while (*c) {
|
||||
if (*c == '\n' || *c == '\r') {
|
||||
@@ -2437,7 +2267,7 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
#ifdef JANET_WINDOWS
|
||||
JanetOverlapped overlapped;
|
||||
OVERLAPPED overlapped;
|
||||
DWORD flags;
|
||||
#ifdef JANET_NET
|
||||
WSABUF wbuf;
|
||||
@@ -2472,7 +2302,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
case JANET_ASYNC_EVENT_FAILED:
|
||||
case JANET_ASYNC_EVENT_COMPLETE: {
|
||||
/* Called when read finished */
|
||||
uint32_t ev_bytes = (uint32_t) state->overlapped.bytes_transfered;
|
||||
uint32_t ev_bytes = (uint32_t) state->overlapped.InternalHigh;
|
||||
state->bytes_read += ev_bytes;
|
||||
if (state->bytes_read == 0 && (state->mode != JANET_ASYNC_READMODE_RECVFROM)) {
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
@@ -2504,7 +2334,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
/* fallthrough */
|
||||
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(JanetOverlapped));
|
||||
memset(&(state->overlapped), 0, sizeof(OVERLAPPED));
|
||||
int status;
|
||||
#ifdef JANET_NET
|
||||
if (state->mode == JANET_ASYNC_READMODE_RECVFROM) {
|
||||
@@ -2512,7 +2342,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
state->wbuf.buf = (char *) state->chunk_buf;
|
||||
state->fromlen = sizeof(state->from);
|
||||
status = WSARecvFrom((SOCKET) stream->handle, &state->wbuf, 1,
|
||||
NULL, &state->flags, &state->from, &state->fromlen, &state->overlapped.as.wsaoverlapped, NULL);
|
||||
NULL, &state->flags, &state->from, &state->fromlen, &state->overlapped, NULL);
|
||||
if (status && (WSA_IO_PENDING != WSAGetLastError())) {
|
||||
janet_cancel(fiber, janet_ev_lasterr());
|
||||
janet_async_end(fiber);
|
||||
@@ -2523,9 +2353,9 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
{
|
||||
/* Some handles (not all) read from the offset in lpOverlapped
|
||||
* if its not set before calling `ReadFile` these streams will always read from offset 0 */
|
||||
state->overlapped.as.overlapped.Offset = (DWORD) state->bytes_read;
|
||||
state->overlapped.Offset = (DWORD) state->bytes_read;
|
||||
|
||||
status = ReadFile(stream->handle, state->chunk_buf, chunk_size, NULL, &state->overlapped.as.overlapped);
|
||||
status = ReadFile(stream->handle, state->chunk_buf, chunk_size, NULL, &state->overlapped);
|
||||
if (!status && (ERROR_IO_PENDING != GetLastError())) {
|
||||
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
||||
if (state->bytes_read) {
|
||||
@@ -2681,7 +2511,7 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
#ifdef JANET_WINDOWS
|
||||
JanetOverlapped overlapped;
|
||||
OVERLAPPED overlapped;
|
||||
DWORD flags;
|
||||
#ifdef JANET_NET
|
||||
WSABUF wbuf;
|
||||
@@ -2722,7 +2552,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
case JANET_ASYNC_EVENT_FAILED:
|
||||
case JANET_ASYNC_EVENT_COMPLETE: {
|
||||
/* Called when write finished */
|
||||
uint32_t ev_bytes = (uint32_t) state->overlapped.bytes_transfered;
|
||||
uint32_t ev_bytes = (uint32_t) state->overlapped.InternalHigh;
|
||||
if (ev_bytes == 0 && (state->mode != JANET_ASYNC_WRITEMODE_SENDTO)) {
|
||||
janet_cancel(fiber, janet_cstringv("disconnect"));
|
||||
janet_async_end(fiber);
|
||||
@@ -2751,7 +2581,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
bytes = state->src.str;
|
||||
len = janet_string_length(bytes);
|
||||
}
|
||||
memset(&(state->overlapped), 0, sizeof(JanetOverlapped));
|
||||
memset(&(state->overlapped), 0, sizeof(WSAOVERLAPPED));
|
||||
|
||||
int status;
|
||||
#ifdef JANET_NET
|
||||
@@ -2761,7 +2591,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
state->wbuf.len = len;
|
||||
const struct sockaddr *to = state->dest_abst;
|
||||
int tolen = (int) janet_abstract_size((void *) to);
|
||||
status = WSASendTo(sock, &state->wbuf, 1, NULL, state->flags, to, tolen, &state->overlapped.as.wsaoverlapped, NULL);
|
||||
status = WSASendTo(sock, &state->wbuf, 1, NULL, state->flags, to, tolen, &state->overlapped, NULL);
|
||||
if (status) {
|
||||
if (WSA_IO_PENDING == WSAGetLastError()) {
|
||||
janet_async_in_flight(fiber);
|
||||
@@ -2784,9 +2614,9 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
* for more details see the lpOverlapped parameter in
|
||||
* https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
|
||||
*/
|
||||
state->overlapped.as.overlapped.Offset = (DWORD) 0xFFFFFFFF;
|
||||
state->overlapped.as.overlapped.OffsetHigh = (DWORD) 0xFFFFFFFF;
|
||||
status = WriteFile(stream->handle, bytes, len, NULL, &state->overlapped.as.overlapped);
|
||||
state->overlapped.Offset = (DWORD) 0xFFFFFFFF;
|
||||
state->overlapped.OffsetHigh = (DWORD) 0xFFFFFFFF;
|
||||
status = WriteFile(stream->handle, bytes, len, NULL, &state->overlapped);
|
||||
if (!status) {
|
||||
if (ERROR_IO_PENDING == GetLastError()) {
|
||||
janet_async_in_flight(fiber);
|
||||
@@ -2942,11 +2772,10 @@ int janet_make_pipe(JanetHandle handles[2], int mode) {
|
||||
if (!CreatePipe(handles, handles + 1, &saAttr, 0)) return -1;
|
||||
return 0;
|
||||
}
|
||||
snprintf(PipeNameBuffer,
|
||||
sizeof(PipeNameBuffer),
|
||||
"\\\\.\\Pipe\\JanetPipeFile.%08x.%08x",
|
||||
(unsigned int) GetCurrentProcessId(),
|
||||
(unsigned int) InterlockedIncrement(&PipeSerialNumber));
|
||||
sprintf(PipeNameBuffer,
|
||||
"\\\\.\\Pipe\\JanetPipeFile.%08x.%08x",
|
||||
(unsigned int) GetCurrentProcessId(),
|
||||
(unsigned int) InterlockedIncrement(&PipeSerialNumber));
|
||||
|
||||
/* server handle goes to subprocess */
|
||||
shandle = CreateNamedPipeA(
|
||||
@@ -3002,14 +2831,12 @@ error:
|
||||
|
||||
JANET_CORE_FN(cfun_ev_go,
|
||||
"(ev/go fiber-or-fun &opt value supervisor)",
|
||||
"Put a fiber on the event loop to be resumed later. If a "
|
||||
"function is used, it is wrapped with `fiber/new` first. "
|
||||
"Returns a task fiber. Optionally pass a value to resume "
|
||||
"with, otherwise resumes with nil. An optional `core/channel` "
|
||||
"can be provided as a supervisor. When various events occur "
|
||||
"in the newly scheduled fiber, an event will be pushed to the "
|
||||
"supervisor. If not provided, the new fiber will inherit the "
|
||||
"current supervisor.") {
|
||||
"Put a fiber on the event loop to be resumed later. If a function is used, it is wrapped "
|
||||
"with `fiber/new` first. "
|
||||
"Optionally pass a value to resume with, otherwise resumes with nil. Returns the fiber. "
|
||||
"An optional `core/channel` can be provided as a supervisor. When various "
|
||||
"events occur in the newly scheduled fiber, an event will be pushed to the supervisor. "
|
||||
"If not provided, the new fiber will inherit the current supervisor.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
Janet value = argc >= 2 ? argv[1] : janet_wrap_nil();
|
||||
void *supervisor = janet_optabstract(argv, argc, 2, &janet_channel_type, janet_vm.root_fiber->supervisor_channel);
|
||||
@@ -3035,9 +2862,6 @@ JANET_CORE_FN(cfun_ev_go,
|
||||
fiber->env->proto = janet_vm.fiber->env;
|
||||
} else {
|
||||
fiber = janet_getfiber(argv, 0);
|
||||
if (janet_fiber_status(fiber) != JANET_STATUS_NEW) {
|
||||
janet_panic("can only schedule new fibers where (= (fiber/status f) :new)");
|
||||
}
|
||||
}
|
||||
fiber->supervisor_channel = supervisor;
|
||||
janet_schedule(fiber, value);
|
||||
@@ -3082,8 +2906,7 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
|
||||
uint32_t count1;
|
||||
memcpy(&count1, nextbytes, sizeof(count1));
|
||||
size_t count = (size_t) count1;
|
||||
/* Use division to avoid overflowing size_t */
|
||||
if (count > (endbytes - nextbytes - sizeof(count1)) / sizeof(JanetCFunRegistry)) {
|
||||
if (count > (endbytes - nextbytes) * sizeof(JanetCFunRegistry)) {
|
||||
janet_panic("thread message invalid");
|
||||
}
|
||||
janet_vm.registry_count = count;
|
||||
@@ -3173,7 +2996,6 @@ JANET_CORE_FN(cfun_ev_thread,
|
||||
"* `:t` - set the task-id of the new thread to value. The task-id is passed in messages to the supervisor channel.\n"
|
||||
"* `:a` - don't copy abstract registry to new thread (performance optimization)\n"
|
||||
"* `:c` - don't copy cfunction registry to new thread (performance optimization)") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_THREADS);
|
||||
janet_arity(argc, 1, 4);
|
||||
Janet value = argc >= 2 ? argv[1] : janet_wrap_nil();
|
||||
if (!janet_checktype(argv[0], JANET_FUNCTION)) janet_getfiber(argv, 0);
|
||||
@@ -3242,7 +3064,6 @@ JANET_NO_RETURN void janet_sleep_await(double sec) {
|
||||
to.is_error = 0;
|
||||
to.sched_id = to.fiber->sched_id;
|
||||
to.curr_fiber = NULL;
|
||||
to.has_worker = 0;
|
||||
add_timeout(to);
|
||||
janet_await();
|
||||
}
|
||||
@@ -3256,74 +3077,33 @@ JANET_CORE_FN(cfun_ev_sleep,
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_ev_deadline,
|
||||
"(ev/deadline sec &opt tocancel tocheck intr?)",
|
||||
"Schedules the event loop to try to cancel the `tocancel` task as with `ev/cancel`. "
|
||||
"After `sec` seconds, the event loop will attempt cancellation of `tocancel` if the "
|
||||
"`tocheck` fiber is resumable. `sec` is a number that can have a fractional part. "
|
||||
"`tocancel` defaults to `(fiber/root)`, but if specified, must be a task (root "
|
||||
"fiber). `tocheck` defaults to `(fiber/current)`, but if specified, must be a fiber. "
|
||||
"Returns `tocancel` immediately. If `interrupt?` is set to true, will create a "
|
||||
"background thread to try to interrupt the VM if the timeout expires.") {
|
||||
janet_arity(argc, 1, 4);
|
||||
"(ev/deadline sec &opt tocancel tocheck)",
|
||||
"Schedules the event loop to try to cancel the `tocancel` "
|
||||
"task as with `ev/cancel`. After `sec` seconds, the event "
|
||||
"loop will attempt cancellation of `tocancel` if the "
|
||||
"`tocheck` fiber is resumable. `sec` is a number that can "
|
||||
"have a fractional part. `tocancel` defaults to "
|
||||
"`(fiber/root)`, but if specified, must be a task (root "
|
||||
"fiber). `tocheck` defaults to `(fiber/current)`, but if "
|
||||
"specified, should be a fiber. Returns `tocancel` "
|
||||
"immediately.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
double sec = janet_getnumber(argv, 0);
|
||||
sec = (sec < 0) ? 0 : sec;
|
||||
JanetFiber *tocancel = janet_optfiber(argv, argc, 1, janet_vm.root_fiber);
|
||||
JanetFiber *tocheck = janet_optfiber(argv, argc, 2, janet_vm.fiber);
|
||||
int use_interrupt = janet_optboolean(argv, argc, 3, 0);
|
||||
JanetTimeout to;
|
||||
to.when = ts_delta(ts_now(), sec);
|
||||
to.fiber = tocancel;
|
||||
to.curr_fiber = tocheck;
|
||||
to.is_error = 0;
|
||||
to.sched_id = to.fiber->sched_id;
|
||||
if (use_interrupt) {
|
||||
#ifdef JANET_ANDROID
|
||||
janet_sandbox_assert(JANET_SANDBOX_SIGNAL);
|
||||
#endif
|
||||
JanetThreadedTimeout *tto = janet_malloc(sizeof(JanetThreadedTimeout));
|
||||
if (NULL == tto) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
tto->sec = sec;
|
||||
tto->vm = &janet_vm;
|
||||
tto->fiber = tocheck;
|
||||
#ifdef JANET_WINDOWS
|
||||
HANDLE cancel_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (NULL == cancel_event) {
|
||||
janet_free(tto);
|
||||
janet_panic("failed to create cancel event");
|
||||
}
|
||||
tto->cancel_event = cancel_event;
|
||||
HANDLE worker = CreateThread(NULL, 0, janet_timeout_body, tto, CREATE_SUSPENDED, NULL);
|
||||
if (NULL == worker) {
|
||||
janet_free(tto);
|
||||
janet_panic("failed to create thread");
|
||||
}
|
||||
#else
|
||||
pthread_t worker;
|
||||
int err = pthread_create(&worker, NULL, janet_timeout_body, tto);
|
||||
if (err) {
|
||||
janet_free(tto);
|
||||
janet_panicf("%s", janet_strerror(err));
|
||||
}
|
||||
#endif
|
||||
to.has_worker = 1;
|
||||
to.worker = worker;
|
||||
#ifdef JANET_WINDOWS
|
||||
to.worker_event = cancel_event;
|
||||
ResumeThread(worker);
|
||||
#endif
|
||||
} else {
|
||||
to.has_worker = 0;
|
||||
}
|
||||
add_timeout(to);
|
||||
return janet_wrap_fiber(tocancel);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_ev_cancel,
|
||||
"(ev/cancel fiber err)",
|
||||
"Cancel a suspended task fiber in the event loop. Differs from "
|
||||
"`cancel` in that it returns the canceled fiber immediately.") {
|
||||
"Cancel a suspended fiber in the event loop. Differs from cancel in that it returns the canceled fiber immediately.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
Janet err = argv[1];
|
||||
@@ -3556,7 +3336,7 @@ JANET_CORE_FN(janet_cfun_to_file,
|
||||
|
||||
JANET_CORE_FN(janet_cfun_ev_all_tasks,
|
||||
"(ev/all-tasks)",
|
||||
"Get an array of all active task fibers that are being used by the scheduler.") {
|
||||
"Get an array of all active fibers that are being used by the scheduler.") {
|
||||
janet_fixarity(argc, 0);
|
||||
(void) argv;
|
||||
JanetArray *array = janet_array(janet_vm.active_tasks.count);
|
||||
@@ -3608,6 +3388,8 @@ void janet_lib_ev(JanetTable *env) {
|
||||
janet_register_abstract_type(&janet_channel_type);
|
||||
janet_register_abstract_type(&janet_mutex_type);
|
||||
janet_register_abstract_type(&janet_rwlock_type);
|
||||
|
||||
janet_lib_filewatch(env);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -1344,15 +1344,6 @@ typedef double (win64_variant_f_ffif)(double, double, uint64_t, double);
|
||||
typedef double (win64_variant_f_fffi)(double, double, double, uint64_t);
|
||||
typedef double (win64_variant_f_ffff)(double, double, double, double);
|
||||
|
||||
/* MSVC stack frame runtime error checking (/RTCs) prepends alloca() allocations with an _RTC_ALLOCA_NODE
|
||||
* header; misalligning stack-based FFI arguments and causing the memmove() (by stack_shift) to corrupt
|
||||
* the _RTC_ALLOCA_NODE header.
|
||||
*
|
||||
* We turn off the RTC-instrumented alloca() and adding of _RTC_CheckStackVars to function prologue just
|
||||
* for janet_ffi_win64() */
|
||||
#ifdef __MSVC_RUNTIME_CHECKS
|
||||
#pragma runtime_checks( "s", off )
|
||||
#endif
|
||||
static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointer, const Janet *argv) {
|
||||
union {
|
||||
uint64_t integer;
|
||||
@@ -1502,10 +1493,6 @@ static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointe
|
||||
|
||||
return janet_ffi_read_one(ret_mem, signature->ret.type, JANET_FFI_MAX_RECUR);
|
||||
}
|
||||
#ifdef __MSVC_RUNTIME_CHECKS
|
||||
// Restore stack frame runtime error checking (/RTCs) if it was enabled.
|
||||
#pragma runtime_checks ( "s", restore )
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1699,7 +1686,7 @@ JANET_CORE_FN(cfun_ffi_buffer_write,
|
||||
JanetFFIType type = decode_ffi_type(argv[0]);
|
||||
uint32_t el_size = (uint32_t) type_size(type);
|
||||
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size);
|
||||
int32_t index = janet_optnat(argv, argc, 3, buffer->count);
|
||||
int32_t index = janet_optnat(argv, argc, 3, 0);
|
||||
int32_t old_count = buffer->count;
|
||||
if (index > old_count) janet_panic("index out of bounds");
|
||||
buffer->count = index;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -592,8 +592,8 @@ JANET_CORE_FN(cfun_fiber_status,
|
||||
"* :user(0-7) - the fiber is suspended by a user signal\n"
|
||||
"* :interrupted - the fiber was interrupted\n"
|
||||
"* :suspended - the fiber is waiting to be resumed by the scheduler\n"
|
||||
"* :new - the fiber has just been created and not yet run\n"
|
||||
"* :alive - the fiber is currently running and cannot be resumed") {
|
||||
"* :alive - the fiber is currently running and cannot be resumed\n"
|
||||
"* :new - the fiber has just been created and not yet run") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
uint32_t s = janet_fiber_status(fiber);
|
||||
@@ -610,9 +610,8 @@ JANET_CORE_FN(cfun_fiber_current,
|
||||
|
||||
JANET_CORE_FN(cfun_fiber_root,
|
||||
"(fiber/root)",
|
||||
"Returns the current root fiber. The root fiber is the oldest "
|
||||
"ancestor that does not have a parent. Note that a root fiber "
|
||||
"is also a task fiber.") {
|
||||
"Returns the current root fiber. The root fiber is the oldest ancestor "
|
||||
"that does not have a parent.") {
|
||||
(void) argv;
|
||||
janet_fixarity(argc, 0);
|
||||
return janet_wrap_fiber(janet_vm.root_fiber);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -326,7 +326,7 @@ static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uin
|
||||
#define FILE_INFO_PADDING (4096 * 4)
|
||||
|
||||
typedef struct {
|
||||
JanetOverlapped overlapped;
|
||||
OVERLAPPED overlapped;
|
||||
JanetStream *stream;
|
||||
JanetWatcher *watcher;
|
||||
JanetFiber *fiber;
|
||||
@@ -456,7 +456,7 @@ static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t
|
||||
Janet pathv = janet_wrap_string(ow->dir_path);
|
||||
ow->flags = flags | watcher->default_flags;
|
||||
ow->watcher = watcher;
|
||||
ow->overlapped.as.overlapped.hEvent = CreateEvent(NULL, FALSE, 0, NULL); /* Do we need this */
|
||||
ow->overlapped.hEvent = CreateEvent(NULL, FALSE, 0, NULL); /* Do we need this */
|
||||
Janet streamv = janet_wrap_pointer(ow);
|
||||
janet_table_put(watcher->watch_descriptors, pathv, streamv);
|
||||
if (watcher->is_watching) {
|
||||
@@ -521,23 +521,23 @@ static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t
|
||||
(void) watcher;
|
||||
(void) flags;
|
||||
(void) path;
|
||||
janet_panic("filewatch not supported on this platform");
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
static void janet_watcher_remove(JanetWatcher *watcher, const char *path) {
|
||||
(void) watcher;
|
||||
(void) path;
|
||||
janet_panic("filewatch not supported on this platform");
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
static void janet_watcher_listen(JanetWatcher *watcher) {
|
||||
(void) watcher;
|
||||
janet_panic("filewatch not supported on this platform");
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
static void janet_watcher_unlisten(JanetWatcher *watcher) {
|
||||
(void) watcher;
|
||||
janet_panic("filewatch not supported on this platform");
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -573,7 +573,7 @@ static const JanetAbstractType janet_filewatch_at = {
|
||||
};
|
||||
|
||||
JANET_CORE_FN(cfun_filewatch_make,
|
||||
"(filewatch/new channel & default-flags)",
|
||||
"(filewatch/new channel &opt default-flags)",
|
||||
"Create a new filewatcher that will give events to a channel channel. See `filewatch/add` for available flags.\n\n"
|
||||
"When an event is triggered by the filewatcher, a struct containing information will be given to channel as with `ev/give`. "
|
||||
"The contents of the channel depend on the OS, but will contain some common keys:\n\n"
|
||||
@@ -597,35 +597,35 @@ JANET_CORE_FN(cfun_filewatch_make,
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_filewatch_add,
|
||||
"(filewatch/add watcher path flag & more-flags)",
|
||||
"(filewatch/add watcher path &opt flags)",
|
||||
"Add a path to the watcher. Available flags depend on the current OS, and are as follows:\n\n"
|
||||
"Windows/MINGW (flags correspond to `FILE_NOTIFY_CHANGE_*` flags in win32 documentation):\n\n"
|
||||
"Windows/MINGW (flags correspond to FILE_NOTIFY_CHANGE_* flags in win32 documentation):\n\n"
|
||||
"* `:all` - trigger an event for all of the below triggers.\n\n"
|
||||
"* `:attributes` - `FILE_NOTIFY_CHANGE_ATTRIBUTES`\n\n"
|
||||
"* `:creation` - `FILE_NOTIFY_CHANGE_CREATION`\n\n"
|
||||
"* `:dir-name` - `FILE_NOTIFY_CHANGE_DIR_NAME`\n\n"
|
||||
"* `:last-access` - `FILE_NOTIFY_CHANGE_LAST_ACCESS`\n\n"
|
||||
"* `:last-write` - `FILE_NOTIFY_CHANGE_LAST_WRITE`\n\n"
|
||||
"* `:security` - `FILE_NOTIFY_CHANGE_SECURITY`\n\n"
|
||||
"* `:size` - `FILE_NOTIFY_CHANGE_SIZE`\n\n"
|
||||
"* `:attributes` - FILE_NOTIFY_CHANGE_ATTRIBUTES\n\n"
|
||||
"* `:creation` - FILE_NOTIFY_CHANGE_CREATION\n\n"
|
||||
"* `:dir-name` - FILE_NOTIFY_CHANGE_DIR_NAME\n\n"
|
||||
"* `:last-access` - FILE_NOTIFY_CHANGE_LAST_ACCESS\n\n"
|
||||
"* `:last-write` - FILE_NOTIFY_CHANGE_LAST_WRITE\n\n"
|
||||
"* `:security` - FILE_NOTIFY_CHANGE_SECURITY\n\n"
|
||||
"* `:size` - FILE_NOTIFY_CHANGE_SIZE\n\n"
|
||||
"* `:recursive` - watch subdirectories recursively\n\n"
|
||||
"Linux (flags correspond to `IN_*` flags from <sys/inotify.h>):\n\n"
|
||||
"* `:access` - `IN_ACCESS`\n\n"
|
||||
"* `:all` - `IN_ALL_EVENTS`\n\n"
|
||||
"* `:attrib` - `IN_ATTRIB`\n\n"
|
||||
"* `:close-nowrite` - `IN_CLOSE_NOWRITE`\n\n"
|
||||
"* `:close-write` - `IN_CLOSE_WRITE`\n\n"
|
||||
"* `:create` - `IN_CREATE`\n\n"
|
||||
"* `:delete` - `IN_DELETE`\n\n"
|
||||
"* `:delete-self` - `IN_DELETE_SELF`\n\n"
|
||||
"* `:ignored` - `IN_IGNORED`\n\n"
|
||||
"* `:modify` - `IN_MODIFY`\n\n"
|
||||
"* `:move-self` - `IN_MOVE_SELF`\n\n"
|
||||
"* `:moved-from` - `IN_MOVED_FROM`\n\n"
|
||||
"* `:moved-to` - `IN_MOVED_TO`\n\n"
|
||||
"* `:open` - `IN_OPEN`\n\n"
|
||||
"* `:q-overflow` - `IN_Q_OVERFLOW`\n\n"
|
||||
"* `:unmount` - `IN_UNMOUNT`\n\n\n"
|
||||
"Linux (flags correspond to IN_* flags from <sys/inotify.h>):\n\n"
|
||||
"* `:access` - IN_ACCESS\n\n"
|
||||
"* `:all` - IN_ALL_EVENTS\n\n"
|
||||
"* `:attrib` - IN_ATTRIB\n\n"
|
||||
"* `:close-nowrite` - IN_CLOSE_NOWRITE\n\n"
|
||||
"* `:close-write` - IN_CLOSE_WRITE\n\n"
|
||||
"* `:create` - IN_CREATE\n\n"
|
||||
"* `:delete` - IN_DELETE\n\n"
|
||||
"* `:delete-self` - IN_DELETE_SELF\n\n"
|
||||
"* `:ignored` - IN_IGNORED\n\n"
|
||||
"* `:modify` - IN_MODIFY\n\n"
|
||||
"* `:move-self` - IN_MOVE_SELF\n\n"
|
||||
"* `:moved-from` - IN_MOVED_FROM\n\n"
|
||||
"* `:moved-to` - IN_MOVED_TO\n\n"
|
||||
"* `:open` - IN_OPEN\n\n"
|
||||
"* `:q-overflow` - IN_Q_OVERFLOW\n\n"
|
||||
"* `:unmount` - IN_UNMOUNT\n\n\n"
|
||||
"On Windows, events will have the following possible types:\n\n"
|
||||
"* `:unknown`\n\n"
|
||||
"* `:added`\n\n"
|
||||
@@ -633,7 +633,7 @@ JANET_CORE_FN(cfun_filewatch_add,
|
||||
"* `:modified`\n\n"
|
||||
"* `:renamed-old`\n\n"
|
||||
"* `:renamed-new`\n\n"
|
||||
"On Linux, events will have a `:type` corresponding to the possible flags, excluding `:all`.\n"
|
||||
"On Linux, events will a `:type` corresponding to the possible flags, excluding `:all`.\n"
|
||||
"") {
|
||||
janet_arity(argc, 2, -1);
|
||||
JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -346,9 +346,6 @@ static void janet_deinit_block(JanetGCObject *mem) {
|
||||
break;
|
||||
case JANET_MEMORY_ABSTRACT: {
|
||||
JanetAbstractHead *head = (JanetAbstractHead *)mem;
|
||||
if (head->type->gcperthread) {
|
||||
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
|
||||
}
|
||||
if (head->type->gc) {
|
||||
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
|
||||
}
|
||||
@@ -500,11 +497,15 @@ void janet_sweep() {
|
||||
/* If not visited... */
|
||||
if (!janet_truthy(items[i].value)) {
|
||||
void *abst = janet_unwrap_abstract(items[i].key);
|
||||
JanetAbstractHead *head = janet_abstract_head(abst);
|
||||
if (head->type->gcperthread) {
|
||||
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
|
||||
if (0 == janet_abstract_decref(abst)) {
|
||||
/* Run finalizer */
|
||||
JanetAbstractHead *head = janet_abstract_head(abst);
|
||||
if (head->type->gc) {
|
||||
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
|
||||
}
|
||||
/* Free memory */
|
||||
janet_free(janet_abstract_head(abst));
|
||||
}
|
||||
janet_abstract_decref_maybe_free(abst);
|
||||
|
||||
/* Mark as tombstone in place */
|
||||
items[i].key = janet_wrap_nil();
|
||||
@@ -671,11 +672,13 @@ void janet_clear_memory(void) {
|
||||
for (int32_t i = 0; i < janet_vm.threaded_abstracts.capacity; i++) {
|
||||
if (janet_checktype(items[i].key, JANET_ABSTRACT)) {
|
||||
void *abst = janet_unwrap_abstract(items[i].key);
|
||||
JanetAbstractHead *head = janet_abstract_head(abst);
|
||||
if (head->type->gcperthread) {
|
||||
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
|
||||
if (0 == janet_abstract_decref(abst)) {
|
||||
JanetAbstractHead *head = janet_abstract_head(abst);
|
||||
if (head->type->gc) {
|
||||
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
|
||||
}
|
||||
janet_free(janet_abstract_head(abst));
|
||||
}
|
||||
janet_abstract_decref_maybe_free(abst);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Calvin Rose & contributors
|
||||
* Copyright (c) 2024 Calvin Rose & contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -191,21 +191,21 @@ Janet janet_wrap_u64(uint64_t x) {
|
||||
|
||||
JANET_CORE_FN(cfun_it_s64_new,
|
||||
"(int/s64 value)",
|
||||
"Create a boxed signed 64 bit integer from a string value or a number.") {
|
||||
"Create a boxed signed 64 bit integer from a string value.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_s64(janet_unwrap_s64(argv[0]));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_it_u64_new,
|
||||
"(int/u64 value)",
|
||||
"Create a boxed unsigned 64 bit integer from a string value or a number.") {
|
||||
"Create a boxed unsigned 64 bit integer from a string value.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_u64(janet_unwrap_u64(argv[0]));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_to_number,
|
||||
"(int/to-number value)",
|
||||
"Convert an int/u64 or int/s64 to a number. Fails if the number is out of range for an int64.") {
|
||||
"Convert an int/u64 or int/s64 to a number. Fails if the number is out of range for an int32.") {
|
||||
janet_fixarity(argc, 1);
|
||||
if (janet_type(argv[0]) == JANET_ABSTRACT) {
|
||||
void *abst = janet_unwrap_abstract(argv[0]);
|
||||
@@ -333,7 +333,7 @@ static int compare_uint64_double(uint64_t x, double y) {
|
||||
}
|
||||
}
|
||||
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_s64_compare(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
if (janet_is_int(argv[0]) != JANET_INT_S64) {
|
||||
janet_panic("compare method requires int/s64 as first argument");
|
||||
@@ -368,7 +368,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_s64_compare(int32_t argc, Janet *argv
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
if (janet_is_int(argv[0]) != JANET_INT_U64) {
|
||||
janet_panic("compare method requires int/u64 as first argument");
|
||||
@@ -416,7 +416,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_u64_compare(int32_t argc, Janet *argv
|
||||
* This will not affect the end result (property of twos complement).
|
||||
*/
|
||||
#define OPMETHOD(T, type, name, oper) \
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
janet_arity(argc, 2, -1); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[0]); \
|
||||
@@ -427,7 +427,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name(int32_t argc, Janet *
|
||||
} \
|
||||
|
||||
#define OPMETHODINVERT(T, type, name, oper) \
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
|
||||
static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
|
||||
janet_fixarity(argc, 2); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[1]); \
|
||||
@@ -437,7 +437,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name##i(int32_t argc, Jane
|
||||
} \
|
||||
|
||||
#define UNARYMETHOD(T, type, name, oper) \
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
janet_fixarity(argc, 1); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = oper(janet_unwrap_##type(argv[0])); \
|
||||
@@ -450,7 +450,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name(int32_t argc, Janet *
|
||||
#define DIVZERO_mod return janet_wrap_abstract(box)
|
||||
|
||||
#define DIVMETHOD(T, type, name, oper) \
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
janet_arity(argc, 2, -1); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[0]); \
|
||||
@@ -463,7 +463,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name(int32_t argc, Janet *
|
||||
} \
|
||||
|
||||
#define DIVMETHODINVERT(T, type, name, oper) \
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
|
||||
static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
|
||||
janet_fixarity(argc, 2); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[1]); \
|
||||
@@ -474,7 +474,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name##i(int32_t argc, Jane
|
||||
} \
|
||||
|
||||
#define DIVMETHOD_SIGNED(T, type, name, oper) \
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
janet_arity(argc, 2, -1); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[0]); \
|
||||
@@ -488,7 +488,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name(int32_t argc, Janet *
|
||||
} \
|
||||
|
||||
#define DIVMETHODINVERT_SIGNED(T, type, name, oper) \
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
|
||||
static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
|
||||
janet_fixarity(argc, 2); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[1]); \
|
||||
@@ -499,7 +499,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_##type##_##name##i(int32_t argc, Jane
|
||||
return janet_wrap_abstract(box); \
|
||||
} \
|
||||
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_s64_divf(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_it_s64_divf(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
|
||||
int64_t op1 = janet_unwrap_s64(argv[0]);
|
||||
@@ -510,7 +510,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_s64_divf(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_abstract(box);
|
||||
}
|
||||
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_s64_divfi(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_it_s64_divfi(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
|
||||
int64_t op2 = janet_unwrap_s64(argv[0]);
|
||||
@@ -521,7 +521,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_s64_divfi(int32_t argc, Janet *argv)
|
||||
return janet_wrap_abstract(box);
|
||||
}
|
||||
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_s64_mod(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_it_s64_mod(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
|
||||
int64_t op1 = janet_unwrap_s64(argv[0]);
|
||||
@@ -535,7 +535,7 @@ static JANET_CFUNCTION_ALIGN Janet cfun_it_s64_mod(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_abstract(box);
|
||||
}
|
||||
|
||||
static JANET_CFUNCTION_ALIGN Janet cfun_it_s64_modi(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_it_s64_modi(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
|
||||
int64_t op2 = janet_unwrap_s64(argv[0]);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -43,7 +43,6 @@ static void *io_file_unmarshal(JanetMarshalContext *ctx);
|
||||
static Janet io_file_next(void *p, Janet key);
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <io.h>
|
||||
#define ftell _ftelli64
|
||||
#define fseek _fseeki64
|
||||
#endif
|
||||
@@ -110,12 +109,11 @@ static int32_t checkflags(const uint8_t *str) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void *makef(FILE *f, int32_t flags, size_t bufsize) {
|
||||
static void *makef(FILE *f, int32_t flags) {
|
||||
JanetFile *iof = (JanetFile *) janet_abstract(&janet_file_type, sizeof(JanetFile));
|
||||
iof->file = f;
|
||||
iof->flags = flags;
|
||||
iof->vbufsize = bufsize;
|
||||
#if !(defined(JANET_WINDOWS) || defined(JANET_PLAN9))
|
||||
#ifndef JANET_WINDOWS
|
||||
/* While we would like fopen to set cloexec by default (like O_CLOEXEC) with the e flag, that is
|
||||
* not standard. */
|
||||
if (!(flags & JANET_FILE_NOT_CLOSEABLE))
|
||||
@@ -166,9 +164,8 @@ JANET_CORE_FN(cfun_io_fopen,
|
||||
flags = JANET_FILE_READ;
|
||||
}
|
||||
FILE *f = fopen((const char *)fname, (const char *)fmode);
|
||||
size_t bufsize = BUFSIZ;
|
||||
if (f != NULL) {
|
||||
#if !(defined(JANET_WINDOWS) || defined(JANET_PLAN9))
|
||||
#ifndef JANET_WINDOWS
|
||||
struct stat st;
|
||||
fstat(fileno(f), &st);
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
@@ -176,7 +173,7 @@ JANET_CORE_FN(cfun_io_fopen,
|
||||
janet_panicf("cannot open directory: %s", fname);
|
||||
}
|
||||
#endif
|
||||
bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
|
||||
size_t bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
|
||||
if (bufsize != BUFSIZ) {
|
||||
int result = setvbuf(f, NULL, bufsize ? _IOFBF : _IONBF, bufsize);
|
||||
if (result) {
|
||||
@@ -184,7 +181,7 @@ JANET_CORE_FN(cfun_io_fopen,
|
||||
}
|
||||
}
|
||||
}
|
||||
return f ? janet_wrap_abstract(makef(f, flags, bufsize))
|
||||
return f ? janet_makefile(f, flags)
|
||||
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, janet_strerror(errno)), janet_wrap_nil())
|
||||
: janet_wrap_nil();
|
||||
}
|
||||
@@ -252,9 +249,9 @@ JANET_CORE_FN(cfun_io_fread,
|
||||
|
||||
/* Write bytes to a file */
|
||||
JANET_CORE_FN(cfun_io_fwrite,
|
||||
"(file/write f & bytes)",
|
||||
"Writes to a file `f`. Each value of `bytes` must be a "
|
||||
"string, buffer, symbol, or keyword. Returns the file.") {
|
||||
"(file/write f bytes)",
|
||||
"Writes to a file. 'bytes' must be string, buffer, or symbol. Returns the "
|
||||
"file.") {
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
||||
if (iof->flags & JANET_FILE_CLOSED)
|
||||
@@ -413,23 +410,12 @@ static void io_file_marshal(void *p, JanetMarshalContext *ctx) {
|
||||
JanetFile *iof = (JanetFile *)p;
|
||||
if (ctx->flags & JANET_MARSHAL_UNSAFE) {
|
||||
janet_marshal_abstract(ctx, p);
|
||||
int fno = -1;
|
||||
#ifdef JANET_WINDOWS
|
||||
if (iof->flags & JANET_FILE_NOT_CLOSEABLE) {
|
||||
fno = _fileno(iof->file);
|
||||
} else {
|
||||
fno = _dup(_fileno(iof->file));
|
||||
}
|
||||
janet_marshal_int(ctx, _fileno(iof->file));
|
||||
#else
|
||||
if (iof->flags & JANET_FILE_NOT_CLOSEABLE) {
|
||||
fno = fileno(iof->file);
|
||||
} else {
|
||||
fno = dup(fileno(iof->file));
|
||||
}
|
||||
janet_marshal_int(ctx, fileno(iof->file));
|
||||
#endif
|
||||
janet_marshal_int(ctx, fno);
|
||||
janet_marshal_int(ctx, iof->flags);
|
||||
janet_marshal_size(ctx, iof->vbufsize);
|
||||
} else {
|
||||
janet_panic("cannot marshal file in safe mode");
|
||||
}
|
||||
@@ -458,11 +444,6 @@ static void *io_file_unmarshal(JanetMarshalContext *ctx) {
|
||||
} else {
|
||||
iof->flags = flags;
|
||||
}
|
||||
iof->vbufsize = janet_unmarshal_size(ctx);
|
||||
if (iof->vbufsize != BUFSIZ) {
|
||||
int result = setvbuf(iof->file, NULL, iof->vbufsize ? _IOFBF : _IONBF, iof->vbufsize);
|
||||
janet_assert(!result, "unmarshal setvbuf");
|
||||
}
|
||||
return iof;
|
||||
} else {
|
||||
janet_panic("cannot unmarshal file in safe mode");
|
||||
@@ -740,15 +721,8 @@ JANET_CORE_FN(cfun_io_eflush,
|
||||
void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
JanetType xtype;
|
||||
Janet x;
|
||||
if (!name || name[0] == '\0') { /* Allow NULL or empty string to just use dflt_file directly */
|
||||
x = janet_wrap_nil();
|
||||
xtype = JANET_NIL;
|
||||
} else {
|
||||
x = janet_dyn(name);
|
||||
xtype = janet_type(x);
|
||||
}
|
||||
Janet x = janet_dyn(name);
|
||||
JanetType xtype = janet_type(x);
|
||||
switch (xtype) {
|
||||
default:
|
||||
/* Other values simply do nothing */
|
||||
@@ -804,11 +778,11 @@ FILE *janet_getfile(const Janet *argv, int32_t n, int32_t *flags) {
|
||||
}
|
||||
|
||||
JanetFile *janet_makejfile(FILE *f, int32_t flags) {
|
||||
return makef(f, flags, BUFSIZ);
|
||||
return makef(f, flags);
|
||||
}
|
||||
|
||||
Janet janet_makefile(FILE *f, int32_t flags) {
|
||||
return janet_wrap_abstract(makef(f, flags, BUFSIZ));
|
||||
return janet_wrap_abstract(makef(f, flags));
|
||||
}
|
||||
|
||||
JanetAbstract janet_checkfile(Janet j) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -276,8 +276,6 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
|
||||
pushint(st, def->max_arity);
|
||||
pushint(st, def->constants_length);
|
||||
pushint(st, def->bytecode_length);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_NAMEDARGS)
|
||||
pushint(st, def->named_args_count);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASENVS)
|
||||
pushint(st, def->environments_length);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS)
|
||||
@@ -916,7 +914,6 @@ static const uint8_t *unmarshal_one_def(
|
||||
def->sourcemap = NULL;
|
||||
def->symbolmap = NULL;
|
||||
def->symbolmap_length = 0;
|
||||
def->named_args_count = 0;
|
||||
janet_v_push(st->lookup_defs, def);
|
||||
|
||||
/* Set default lengths to zero */
|
||||
@@ -936,8 +933,6 @@ static const uint8_t *unmarshal_one_def(
|
||||
/* Read some lengths */
|
||||
constants_length = readnat(st, &data);
|
||||
bytecode_length = readnat(st, &data);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_NAMEDARGS)
|
||||
def->named_args_count = readnat(st, &data);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASENVS)
|
||||
environments_length = readnat(st, &data);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS)
|
||||
@@ -1698,7 +1693,6 @@ JANET_CORE_FN(cfun_unmarshal,
|
||||
"Unmarshal a value from a buffer. An optional lookup table "
|
||||
"can be provided to allow for aliases to be resolved. Returns the value "
|
||||
"unmarshalled from the buffer.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_UNMARSHAL);
|
||||
janet_arity(argc, 1, 2);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
JanetTable *reg = NULL;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -271,30 +271,28 @@ JANET_DEFINE_MATHOP(cosh, "Returns the hyperbolic cosine of x.")
|
||||
JANET_DEFINE_MATHOP(acosh, "Returns the hyperbolic arccosine of x.")
|
||||
JANET_DEFINE_MATHOP(sin, "Returns the sine of x.")
|
||||
JANET_DEFINE_MATHOP(sinh, "Returns the hyperbolic sine of x.")
|
||||
JANET_DEFINE_MATHOP(asinh, "Returns the hyperbolic arcsine of x.")
|
||||
JANET_DEFINE_MATHOP(tan, "Returns the tangent of x.")
|
||||
JANET_DEFINE_MATHOP(tanh, "Returns the hyperbolic tangent of x.")
|
||||
JANET_DEFINE_MATHOP(atanh, "Returns the hyperbolic arctangent of x.")
|
||||
JANET_DEFINE_MATHOP(exp, "Returns e to the power of x.")
|
||||
JANET_DEFINE_MATHOP(exp2, "Returns 2 to the power of x.")
|
||||
JANET_DEFINE_MATHOP(log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
|
||||
#ifndef JANET_PLAN9
|
||||
JANET_DEFINE_MATHOP(expm1, "Returns e to the power of x minus 1.")
|
||||
JANET_DEFINE_MATHOP(cbrt, "Returns the cube root of x.")
|
||||
JANET_DEFINE_MATHOP(erf, "Returns the error function of x.")
|
||||
JANET_DEFINE_MATHOP(erfc, "Returns the complementary error function of x.")
|
||||
JANET_DEFINE_NAMED_MATHOP("log-gamma", lgamma, "Returns log-gamma(x).")
|
||||
JANET_DEFINE_NAMED_MATHOP("gamma", tgamma, "Returns gamma(x).")
|
||||
JANET_DEFINE_MATHOP(atanh, "Returns the hyperbolic arctangent of x.")
|
||||
JANET_DEFINE_MATHOP(asinh, "Returns the hyperbolic arcsine of x.")
|
||||
#endif
|
||||
JANET_DEFINE_MATHOP(log, "Returns the natural logarithm of x.")
|
||||
JANET_DEFINE_MATHOP(log10, "Returns the log base 10 of x.")
|
||||
JANET_DEFINE_MATHOP(log2, "Returns the log base 2 of x.")
|
||||
JANET_DEFINE_MATHOP(sqrt, "Returns the square root of x.")
|
||||
JANET_DEFINE_MATHOP(cbrt, "Returns the cube root of x.")
|
||||
JANET_DEFINE_MATHOP(ceil, "Returns the smallest integer value number that is not less than x.")
|
||||
JANET_DEFINE_MATHOP(floor, "Returns the largest integer value number that is not greater than x.")
|
||||
JANET_DEFINE_MATHOP(trunc, "Returns the integer between x and 0 nearest to x.")
|
||||
JANET_DEFINE_MATHOP(round, "Returns the integer nearest to x.")
|
||||
JANET_DEFINE_MATHOP(log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
|
||||
JANET_DEFINE_MATHOP(erf, "Returns the error function of x.")
|
||||
JANET_DEFINE_MATHOP(erfc, "Returns the complementary error function of x.")
|
||||
JANET_DEFINE_NAMED_MATHOP("log-gamma", lgamma, "Returns log-gamma(x).")
|
||||
JANET_DEFINE_NAMED_MATHOP("abs", fabs, "Return the absolute value of x.")
|
||||
JANET_DEFINE_NAMED_MATHOP("gamma", tgamma, "Returns gamma(x).")
|
||||
|
||||
#define JANET_DEFINE_MATH2OP(name, fop, signature, doc)\
|
||||
JANET_CORE_FN(janet_##name, signature, doc) {\
|
||||
@@ -307,9 +305,7 @@ JANET_CORE_FN(janet_##name, signature, doc) {\
|
||||
JANET_DEFINE_MATH2OP(atan2, atan2, "(math/atan2 y x)", "Returns the arctangent of y/x. Works even when x is 0.")
|
||||
JANET_DEFINE_MATH2OP(pow, pow, "(math/pow a x)", "Returns a to the power of x.")
|
||||
JANET_DEFINE_MATH2OP(hypot, hypot, "(math/hypot a b)", "Returns c from the equation c^2 = a^2 + b^2.")
|
||||
#ifndef JANET_PLAN9
|
||||
JANET_DEFINE_MATH2OP(nextafter, nextafter, "(math/next x y)", "Returns the next representable floating point value after x in the direction of y.")
|
||||
#endif
|
||||
|
||||
JANET_CORE_FN(janet_not, "(not x)", "Returns the boolean inverse of x.") {
|
||||
janet_fixarity(argc, 1);
|
||||
@@ -390,17 +386,7 @@ void janet_lib_math(JanetTable *env) {
|
||||
JANET_CORE_REG("math/log10", janet_log10),
|
||||
JANET_CORE_REG("math/log2", janet_log2),
|
||||
JANET_CORE_REG("math/sqrt", janet_sqrt),
|
||||
#ifndef JANET_PLAN9
|
||||
JANET_CORE_REG("math/cbrt", janet_cbrt),
|
||||
JANET_CORE_REG("math/gamma", janet_tgamma),
|
||||
JANET_CORE_REG("math/log-gamma", janet_lgamma),
|
||||
JANET_CORE_REG("math/erfc", janet_erfc),
|
||||
JANET_CORE_REG("math/erf", janet_erf),
|
||||
JANET_CORE_REG("math/expm1", janet_expm1),
|
||||
JANET_CORE_REG("math/atanh", janet_atanh),
|
||||
JANET_CORE_REG("math/asinh", janet_asinh),
|
||||
JANET_CORE_REG("math/next", janet_nextafter),
|
||||
#endif
|
||||
JANET_CORE_REG("math/floor", janet_floor),
|
||||
JANET_CORE_REG("math/ceil", janet_ceil),
|
||||
JANET_CORE_REG("math/pow", janet_pow),
|
||||
@@ -408,6 +394,8 @@ void janet_lib_math(JanetTable *env) {
|
||||
JANET_CORE_REG("math/sinh", janet_sinh),
|
||||
JANET_CORE_REG("math/cosh", janet_cosh),
|
||||
JANET_CORE_REG("math/tanh", janet_tanh),
|
||||
JANET_CORE_REG("math/atanh", janet_atanh),
|
||||
JANET_CORE_REG("math/asinh", janet_asinh),
|
||||
JANET_CORE_REG("math/acosh", janet_acosh),
|
||||
JANET_CORE_REG("math/atan2", janet_atan2),
|
||||
JANET_CORE_REG("math/rng", cfun_rng_make),
|
||||
@@ -417,8 +405,14 @@ void janet_lib_math(JanetTable *env) {
|
||||
JANET_CORE_REG("math/hypot", janet_hypot),
|
||||
JANET_CORE_REG("math/exp2", janet_exp2),
|
||||
JANET_CORE_REG("math/log1p", janet_log1p),
|
||||
JANET_CORE_REG("math/gamma", janet_tgamma),
|
||||
JANET_CORE_REG("math/log-gamma", janet_lgamma),
|
||||
JANET_CORE_REG("math/erfc", janet_erfc),
|
||||
JANET_CORE_REG("math/erf", janet_erf),
|
||||
JANET_CORE_REG("math/expm1", janet_expm1),
|
||||
JANET_CORE_REG("math/trunc", janet_trunc),
|
||||
JANET_CORE_REG("math/round", janet_round),
|
||||
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),
|
||||
@@ -441,9 +435,9 @@ void janet_lib_math(JanetTable *env) {
|
||||
JANET_CORE_DEF(env, "math/int32-max", janet_wrap_number(INT32_MAX),
|
||||
"The maximum contiguous integer representable by a 32 bit signed integer");
|
||||
JANET_CORE_DEF(env, "math/int-min", janet_wrap_number(JANET_INTMIN_DOUBLE),
|
||||
"The minimum contiguous integer representable by a double (-(2^53))");
|
||||
"The minimum contiguous integer representable by a double (2^53)");
|
||||
JANET_CORE_DEF(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE),
|
||||
"The maximum contiguous integer representable by a double (2^53)");
|
||||
"The maximum contiguous integer representable by a double (-(2^53))");
|
||||
#ifdef NAN
|
||||
JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)");
|
||||
#else
|
||||
|
||||
278
src/core/net.c
278
src/core/net.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Calvin Rose and contributors.
|
||||
* Copyright (c) 2024 Calvin Rose and contributors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -120,55 +120,7 @@ static void janet_net_socknoblock(JSock s) {
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Allow specifying IPV6 vs. IPV4 (or unix domain socket) */
|
||||
static int net_get_address_family(Janet x) {
|
||||
if (janet_checktype(x, JANET_NIL)) {
|
||||
return AF_UNSPEC;
|
||||
}
|
||||
if (janet_keyeq(x, "ipv4")) {
|
||||
return AF_INET;
|
||||
}
|
||||
if (janet_keyeq(x, "ipv6")) {
|
||||
return AF_INET6;
|
||||
}
|
||||
#ifndef JANET_WINDOWS
|
||||
if (janet_keyeq(x, "unix")) {
|
||||
return AF_UNIX;
|
||||
}
|
||||
#endif
|
||||
return AF_UNSPEC;
|
||||
}
|
||||
|
||||
/* State machine for async connect */
|
||||
#ifdef JANET_WINDOWS
|
||||
|
||||
typedef struct NetStateConnect {
|
||||
/* Only used for ConnectEx */
|
||||
JanetOverlapped overlapped;
|
||||
} NetStateConnect;
|
||||
|
||||
static LPFN_CONNECTEX lazy_get_connectex(JSock sock) {
|
||||
/* Get ConnectEx */
|
||||
if (janet_vm.connect_ex_loaded) {
|
||||
return janet_vm.connect_ex;
|
||||
}
|
||||
GUID guid = WSAID_CONNECTEX;
|
||||
LPFN_CONNECTEX connect_ex_ptr = NULL;
|
||||
DWORD byte_len = 0;
|
||||
int success = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||
(void*)&guid, sizeof(guid),
|
||||
(void*)&connect_ex_ptr, sizeof(connect_ex_ptr),
|
||||
&byte_len, NULL, NULL);
|
||||
if (success) {
|
||||
janet_vm.connect_ex = connect_ex_ptr;
|
||||
} else {
|
||||
janet_vm.connect_ex = NULL;
|
||||
}
|
||||
janet_vm.connect_ex_loaded = 1;
|
||||
return janet_vm.connect_ex;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
JanetStream *stream = fiber->ev_stream;
|
||||
@@ -188,21 +140,15 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
return;
|
||||
}
|
||||
#ifdef JANET_WINDOWS
|
||||
/* We should be using ConnectEx here */
|
||||
int res = 0;
|
||||
int size = sizeof(res);
|
||||
int r = getsockopt((SOCKET)stream->handle, SOL_SOCKET, SO_CONNECT_TIME, (char *)&res, &size);
|
||||
if (r == NO_ERROR && res == 0xFFFFFFFF) {
|
||||
return; /* This apparently indicates we haven't yet gotten a connection */
|
||||
}
|
||||
const int no_error = NO_ERROR;
|
||||
int r = getsockopt((SOCKET)stream->handle, SOL_SOCKET, SO_ERROR, (char *)&res, &size);
|
||||
#else
|
||||
int res = 0;
|
||||
socklen_t size = sizeof(res);
|
||||
socklen_t size = sizeof res;
|
||||
int r = getsockopt(stream->handle, SOL_SOCKET, SO_ERROR, &res, &size);
|
||||
const int no_error = 0;
|
||||
#endif
|
||||
if (r == no_error) {
|
||||
if (r == 0) {
|
||||
if (res == 0) {
|
||||
janet_schedule(fiber, janet_wrap_abstract(stream));
|
||||
} else {
|
||||
@@ -216,8 +162,8 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
janet_async_end(fiber);
|
||||
}
|
||||
|
||||
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream, void *state) {
|
||||
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, state);
|
||||
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. */
|
||||
@@ -225,7 +171,7 @@ static JANET_NO_RETURN void net_sched_connect(JanetStream *stream, void *state)
|
||||
#ifdef JANET_WINDOWS
|
||||
|
||||
typedef struct {
|
||||
JanetOverlapped overlapped;
|
||||
WSAOVERLAPPED overlapped;
|
||||
JanetFunction *function;
|
||||
JanetStream *lstream;
|
||||
JanetStream *astream;
|
||||
@@ -288,7 +234,7 @@ void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
|
||||
Janet err;
|
||||
NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
|
||||
memset(&state->overlapped, 0, sizeof(JanetOverlapped));
|
||||
memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED));
|
||||
memset(&state->buf, 0, 1024);
|
||||
state->function = fun;
|
||||
state->lstream = stream;
|
||||
@@ -309,7 +255,7 @@ static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet
|
||||
JanetStream *astream = make_stream(asock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
|
||||
state->astream = astream;
|
||||
int socksize = sizeof(SOCKADDR_STORAGE) + 16;
|
||||
if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped.as.wsaoverlapped)) {
|
||||
if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped)) {
|
||||
int code = WSAGetLastError();
|
||||
if (code == WSA_IO_PENDING) {
|
||||
/* indicates io is happening async */
|
||||
@@ -395,7 +341,7 @@ static int janet_get_sockettype(Janet *argv, int32_t argc, int32_t n) {
|
||||
/* Needs argc >= offset + 2 */
|
||||
/* For unix paths, just rertuns a single sockaddr and sets *is_unix to 1,
|
||||
* otherwise 0. Also, ignores is_bind when is a unix socket. */
|
||||
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix, socklen_t *sizeout) {
|
||||
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix) {
|
||||
/* Unix socket support - not yet supported on windows. */
|
||||
#ifndef JANET_WINDOWS
|
||||
if (janet_keyeq(argv[offset], "unix")) {
|
||||
@@ -406,14 +352,15 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock
|
||||
}
|
||||
saddr->sun_family = AF_UNIX;
|
||||
size_t path_size = sizeof(saddr->sun_path);
|
||||
snprintf(saddr->sun_path, path_size, "%s", path);
|
||||
*sizeout = sizeof(struct sockaddr_un);
|
||||
#ifdef JANET_LINUX
|
||||
if (path[0] == '@') {
|
||||
saddr->sun_path[0] = '\0';
|
||||
*sizeout = offsetof(struct sockaddr_un, sun_path) + janet_string_length(path);
|
||||
}
|
||||
snprintf(saddr->sun_path + 1, path_size - 1, "%s", path + 1);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
snprintf(saddr->sun_path, path_size, "%s", path);
|
||||
}
|
||||
*is_unix = 1;
|
||||
return (struct addrinfo *) saddr;
|
||||
}
|
||||
@@ -438,11 +385,6 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock
|
||||
janet_panicf("could not get address info: %s", gai_strerror(status));
|
||||
}
|
||||
*is_unix = 0;
|
||||
#ifdef JANET_WINDOWS
|
||||
*sizeout = 0;
|
||||
#else
|
||||
*sizeout = sizeof(struct sockaddr_un);
|
||||
#endif
|
||||
return ai;
|
||||
}
|
||||
|
||||
@@ -463,13 +405,12 @@ JANET_CORE_FN(cfun_net_sockaddr,
|
||||
int socktype = janet_get_sockettype(argv, argc, 2);
|
||||
int is_unix = 0;
|
||||
int make_arr = (argc >= 3 && janet_truthy(argv[3]));
|
||||
socklen_t addrsize = 0;
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, &addrsize);
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix);
|
||||
#ifndef JANET_WINDOWS
|
||||
/* no unix domain socket support on windows yet */
|
||||
if (is_unix) {
|
||||
void *abst = janet_abstract(&janet_address_type, addrsize);
|
||||
memcpy(abst, ai, addrsize);
|
||||
void *abst = janet_abstract(&janet_address_type, sizeof(struct sockaddr_un));
|
||||
memcpy(abst, ai, sizeof(struct sockaddr_un));
|
||||
Janet ret = janet_wrap_abstract(abst);
|
||||
return make_arr ? janet_wrap_array(janet_array_n(&ret, 1)) : ret;
|
||||
}
|
||||
@@ -520,8 +461,7 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
}
|
||||
|
||||
/* Where we're connecting to */
|
||||
socklen_t addrlen = 0;
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, &addrlen);
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix);
|
||||
|
||||
/* Check if we're binding address */
|
||||
struct addrinfo *binding = NULL;
|
||||
@@ -546,6 +486,7 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
/* Create socket */
|
||||
JSock sock = JSOCKDEFAULT;
|
||||
void *addr = NULL;
|
||||
socklen_t addrlen = 0;
|
||||
#ifndef JANET_WINDOWS
|
||||
if (is_unix) {
|
||||
sock = socket(AF_UNIX, socktype | JSOCKFLAGS, 0);
|
||||
@@ -555,6 +496,7 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
janet_panicf("could not create socket: %V", v);
|
||||
}
|
||||
addr = (void *) ai;
|
||||
addrlen = sizeof(struct sockaddr_un);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@@ -601,52 +543,18 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
}
|
||||
|
||||
/* Wrap socket in abstract type JanetStream */
|
||||
uint32_t udp_flag = 0;
|
||||
if (socktype == SOCK_DGRAM) udp_flag = JANET_STREAM_UDPSERVER;
|
||||
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE | udp_flag);
|
||||
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
|
||||
|
||||
/* Set up the socket for non-blocking IO before connecting */
|
||||
janet_net_socknoblock(sock);
|
||||
|
||||
/* Connect to socket */
|
||||
#ifdef JANET_WINDOWS
|
||||
int status = 0;
|
||||
int err = 0;
|
||||
LPFN_CONNECTEX connect_ex = NULL;
|
||||
if (socktype == SOCK_STREAM && ((connect_ex = lazy_get_connectex(sock)))) {
|
||||
/* Prefer ConnecEx as it works well with overlapped IO. */
|
||||
janet_net_socknoblock(sock);
|
||||
NetStateConnect *state = janet_malloc(sizeof(NetStateConnect));
|
||||
memset(state, 0, sizeof(NetStateConnect));
|
||||
BOOL success = connect_ex(sock, addr, addrlen, NULL, 0, NULL, &state->overlapped.as.overlapped);
|
||||
freeaddrinfo(ai);
|
||||
if (success) {
|
||||
/* Did not fail */
|
||||
} else {
|
||||
int err = WSAGetLastError();
|
||||
if (err == ERROR_IO_PENDING) {
|
||||
/* Did not actually fail yet */
|
||||
} else {
|
||||
janet_free(state);
|
||||
Janet lasterr = janet_ev_lasterr();
|
||||
janet_panicf("could not connect socket (ConnectEx): %V", lasterr);
|
||||
}
|
||||
}
|
||||
|
||||
net_sched_connect(stream, state);
|
||||
} else {
|
||||
/* Default to blocking connect if ConnectEx not available */
|
||||
status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
|
||||
err = WSAGetLastError();
|
||||
freeaddrinfo(ai);
|
||||
/* Set up the socket for non-blocking IO after connecting on windows by default */
|
||||
janet_net_socknoblock(sock);
|
||||
}
|
||||
|
||||
int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
|
||||
int err = WSAGetLastError();
|
||||
freeaddrinfo(ai);
|
||||
#else
|
||||
/* Set up the socket for non-blocking IO before connecting */
|
||||
janet_net_socknoblock(sock);
|
||||
int status;
|
||||
do {
|
||||
status = connect(sock, addr, addrlen);
|
||||
} while (status == -1 && errno == EINTR);
|
||||
int status = connect(sock, addr, addrlen);
|
||||
int err = errno;
|
||||
if (is_unix) {
|
||||
janet_free(ai);
|
||||
@@ -655,19 +563,10 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (status == 0) {
|
||||
/* Connect completed synchronously (common for unix domain sockets).
|
||||
* Return the stream directly without scheduling an async wait,
|
||||
* as edge-triggered kqueue may not signal EVFILT_WRITE if the socket
|
||||
* is already connected when registered. */
|
||||
return janet_wrap_abstract(stream);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
#ifdef JANET_WINDOWS
|
||||
if (status == SOCKET_ERROR) {
|
||||
if (err != WSAEWOULDBLOCK) {
|
||||
#else
|
||||
if (status == -1) {
|
||||
if (err != EINPROGRESS) {
|
||||
#endif
|
||||
JSOCKCLOSE(sock);
|
||||
@@ -676,84 +575,20 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
}
|
||||
}
|
||||
|
||||
net_sched_connect(stream, NULL);
|
||||
net_sched_connect(stream);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_net_socket,
|
||||
"(net/socket &opt type address-family)",
|
||||
"Creates a new unbound socket. Type is an optional keyword, "
|
||||
"either a :stream (usually tcp), or :datagram (usually udp). The default is :stream. "
|
||||
"`address-family` should be one of :ipv4 or :ipv6.") {
|
||||
janet_arity(argc, 0, 2);
|
||||
|
||||
int socktype = janet_get_sockettype(argv, argc, 0);
|
||||
|
||||
/* Create socket */
|
||||
JSock sfd = JSOCKDEFAULT;
|
||||
struct addrinfo *ai = NULL;
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = socktype;
|
||||
#ifdef AI_NUMERICSERV
|
||||
hints.ai_flags = AI_NUMERICSERV; /* Explicitly prevent name resolution */
|
||||
#else
|
||||
hints.ai_flags = 0;
|
||||
#endif
|
||||
if (argc >= 2) {
|
||||
hints.ai_family = net_get_address_family(argv[1]);
|
||||
}
|
||||
int status = getaddrinfo(NULL, "0", &hints, &ai);
|
||||
if (status) {
|
||||
janet_panicf("could not get address info: %s", gai_strerror(status));
|
||||
}
|
||||
|
||||
struct addrinfo *rp = NULL;
|
||||
for (rp = ai; rp != NULL; rp = rp->ai_next) {
|
||||
#ifdef JANET_WINDOWS
|
||||
sfd = WSASocketW(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol, NULL, 0, WSA_FLAG_OVERLAPPED);
|
||||
#else
|
||||
sfd = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
|
||||
#endif
|
||||
if (JSOCKVALID(sfd)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(ai);
|
||||
|
||||
if (!JSOCKVALID(sfd)) {
|
||||
Janet v = janet_ev_lasterr();
|
||||
janet_panicf("could not create socket: %V", v);
|
||||
}
|
||||
|
||||
/* Wrap socket in abstract type JanetStream */
|
||||
uint32_t udp_flag = 0;
|
||||
if (socktype == SOCK_DGRAM) udp_flag = JANET_STREAM_UDPSERVER;
|
||||
JanetStream *stream = make_stream(sfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE | udp_flag);
|
||||
|
||||
/* Set up the socket for non-blocking IO */
|
||||
janet_net_socknoblock(sfd);
|
||||
|
||||
return janet_wrap_abstract(stream);
|
||||
}
|
||||
|
||||
static const char *serverify_socket(JSock sfd, int reuse_addr, int reuse_port) {
|
||||
static const char *serverify_socket(JSock sfd) {
|
||||
/* Set various socket options */
|
||||
int enable = 1;
|
||||
if (reuse_addr) {
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(int)) < 0) {
|
||||
return "setsockopt(SO_REUSEADDR) failed";
|
||||
}
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(int)) < 0) {
|
||||
return "setsockopt(SO_REUSEADDR) failed";
|
||||
}
|
||||
if (reuse_port) {
|
||||
#ifdef SO_REUSEPORT
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
|
||||
return "setsockopt(SO_REUSEPORT) failed";
|
||||
}
|
||||
#else
|
||||
(void) reuse_port;
|
||||
#endif
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
|
||||
return "setsockopt(SO_REUSEPORT) failed";
|
||||
}
|
||||
#endif
|
||||
janet_net_socknoblock(sfd);
|
||||
return NULL;
|
||||
}
|
||||
@@ -807,22 +642,19 @@ JANET_CORE_FN(cfun_net_shutdown,
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_net_listen,
|
||||
"(net/listen host port &opt type no-reuse)",
|
||||
"(net/listen host port &opt type)",
|
||||
"Creates a server. Returns a new stream that is neither readable nor "
|
||||
"writeable. Use net/accept or net/accept-loop be to handle connections and start the server. "
|
||||
"The type parameter specifies the type of network connection, either "
|
||||
"a :stream (usually tcp), or :datagram (usually udp). If not specified, the default is "
|
||||
":stream. The host and port arguments are the same as in net/address. The last boolean parameter `no-reuse` will "
|
||||
"disable the use of `SO_REUSEADDR` and `SO_REUSEPORT` when creating a server on some operating systems.") {
|
||||
":stream. The host and port arguments are the same as in net/address.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_NET_LISTEN);
|
||||
janet_arity(argc, 2, 4);
|
||||
janet_arity(argc, 2, 3);
|
||||
|
||||
/* Get host, port, and handler*/
|
||||
int socktype = janet_get_sockettype(argv, argc, 2);
|
||||
int is_unix = 0;
|
||||
socklen_t addrlen = 0;
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix, &addrlen);
|
||||
int reuse = !(argc >= 4 && janet_truthy(argv[3]));
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix);
|
||||
|
||||
JSock sfd = JSOCKDEFAULT;
|
||||
#ifndef JANET_WINDOWS
|
||||
@@ -832,8 +664,8 @@ JANET_CORE_FN(cfun_net_listen,
|
||||
janet_free(ai);
|
||||
janet_panicf("could not create socket: %V", janet_ev_lasterr());
|
||||
}
|
||||
const char *err = serverify_socket(sfd, reuse, 0);
|
||||
if (NULL != err || bind(sfd, (struct sockaddr *)ai, addrlen)) {
|
||||
const char *err = serverify_socket(sfd);
|
||||
if (NULL != err || bind(sfd, (struct sockaddr *)ai, sizeof(struct sockaddr_un))) {
|
||||
JSOCKCLOSE(sfd);
|
||||
janet_free(ai);
|
||||
if (err) {
|
||||
@@ -855,7 +687,7 @@ JANET_CORE_FN(cfun_net_listen,
|
||||
sfd = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
|
||||
#endif
|
||||
if (!JSOCKVALID(sfd)) continue;
|
||||
const char *err = serverify_socket(sfd, reuse, reuse);
|
||||
const char *err = serverify_socket(sfd);
|
||||
if (NULL != err) {
|
||||
JSOCKCLOSE(sfd);
|
||||
continue;
|
||||
@@ -1130,8 +962,6 @@ static const struct sockopt_type sockopt_type_list[] = {
|
||||
#ifndef JANET_NO_IPV6
|
||||
{ "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER },
|
||||
{ "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER },
|
||||
{ "ipv6-multicast-hops", IPPROTO_IPV6, IPV6_MULTICAST_HOPS, JANET_NUMBER },
|
||||
{ "ipv6-unicast-hops", IPPROTO_IPV6, IPV6_UNICAST_HOPS, JANET_NUMBER },
|
||||
#endif
|
||||
{ NULL, 0, 0, JANET_POINTER }
|
||||
};
|
||||
@@ -1148,10 +978,7 @@ JANET_CORE_FN(cfun_net_setsockopt,
|
||||
"- :ip-add-membership string\n"
|
||||
"- :ip-drop-membership string\n"
|
||||
"- :ipv6-join-group string\n"
|
||||
"- :ipv6-leave-group string\n"
|
||||
"- :ipv6-multicast-hops number\n"
|
||||
"- :ipv6-unicast-hops number\n"
|
||||
) {
|
||||
"- :ipv6-leave-group string\n") {
|
||||
janet_arity(argc, 3, 3);
|
||||
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
janet_stream_flags(stream, JANET_STREAM_SOCKET);
|
||||
@@ -1170,7 +997,6 @@ JANET_CORE_FN(cfun_net_setsockopt,
|
||||
}
|
||||
|
||||
union {
|
||||
unsigned char v_uchar;
|
||||
int v_int;
|
||||
struct ip_mreq v_mreq;
|
||||
#ifndef JANET_NO_IPV6
|
||||
@@ -1185,19 +1011,8 @@ JANET_CORE_FN(cfun_net_setsockopt,
|
||||
val.v_int = janet_getboolean(argv, 2);
|
||||
optlen = sizeof(val.v_int);
|
||||
} else if (st->type == JANET_NUMBER) {
|
||||
#if defined(JANET_BSD) || defined(JANET_ILLUMOS)
|
||||
int v_int = janet_getinteger(argv, 2);
|
||||
if (st->optname == IP_MULTICAST_TTL) {
|
||||
val.v_uchar = v_int;
|
||||
optlen = sizeof(val.v_uchar);
|
||||
} else {
|
||||
val.v_int = v_int;
|
||||
optlen = sizeof(val.v_int);
|
||||
}
|
||||
#else
|
||||
val.v_int = janet_getinteger(argv, 2);
|
||||
optlen = sizeof(val.v_int);
|
||||
#endif
|
||||
} else if (st->optname == IP_ADD_MEMBERSHIP || st->optname == IP_DROP_MEMBERSHIP) {
|
||||
const char *addr = janet_getcstring(argv, 2);
|
||||
memset(&val.v_mreq, 0, sizeof val.v_mreq);
|
||||
@@ -1252,7 +1067,6 @@ void janet_lib_net(JanetTable *env) {
|
||||
JanetRegExt net_cfuns[] = {
|
||||
JANET_CORE_REG("net/address", cfun_net_sockaddr),
|
||||
JANET_CORE_REG("net/listen", cfun_net_listen),
|
||||
JANET_CORE_REG("net/socket", cfun_net_socket),
|
||||
JANET_CORE_REG("net/accept", cfun_stream_accept),
|
||||
JANET_CORE_REG("net/accept-loop", cfun_stream_accept_loop),
|
||||
JANET_CORE_REG("net/read", cfun_stream_read),
|
||||
@@ -1276,8 +1090,6 @@ void janet_net_init(void) {
|
||||
#ifdef JANET_WINDOWS
|
||||
WSADATA wsaData;
|
||||
janet_assert(!WSAStartup(MAKEWORD(2, 2), &wsaData), "could not start winsock");
|
||||
janet_vm.connect_ex_loaded = 0;
|
||||
janet_vm.connect_ex = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
431
src/core/os.c
431
src/core/os.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Calvin Rose and contributors.
|
||||
* Copyright (c) 2024 Calvin Rose and contributors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -40,7 +40,6 @@
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <locale.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef JANET_BSD
|
||||
#include <sys/sysctl.h>
|
||||
@@ -56,11 +55,8 @@
|
||||
#include <sys/utime.h>
|
||||
#include <io.h>
|
||||
#include <process.h>
|
||||
#define JANET_SPAWN_CHDIR
|
||||
#else
|
||||
#ifndef JANET_PLAN9
|
||||
#include <spawn.h>
|
||||
#endif
|
||||
#include <utime.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
@@ -69,9 +65,7 @@
|
||||
#ifdef JANET_APPLE
|
||||
#include <crt_externs.h>
|
||||
#define environ (*_NSGetEnviron())
|
||||
#include <AvailabilityMacros.h>
|
||||
int chroot(const char *dirname);
|
||||
#elif !defined(JANET_PLAN9)
|
||||
#else
|
||||
extern char **environ;
|
||||
#endif
|
||||
#ifdef JANET_THREADS
|
||||
@@ -79,26 +73,6 @@ extern char **environ;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Detect availability of posix_spawn_file_actions_addchdir_np. Since
|
||||
* this doesn't seem to follow any standard, just a common extension, we
|
||||
* must enumerate supported systems for availability. Define JANET_SPAWN_NO_CHDIR
|
||||
* to disable this. */
|
||||
#ifndef JANET_SPAWN_NO_CHDIR
|
||||
#ifdef __GLIBC__
|
||||
#define JANET_SPAWN_CHDIR
|
||||
#elif defined(JANET_APPLE)
|
||||
/* The posix_spawn_file_actions_addchdir_np function
|
||||
* has only been implemented since macOS 10.15 */
|
||||
#if defined(MAC_OS_X_VERSION_10_15) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15)
|
||||
#define JANET_SPAWN_CHDIR
|
||||
#else
|
||||
#define JANET_SPAWN_NO_CHDIR
|
||||
#endif
|
||||
#elif defined(__FreeBSD__) /* Not all BSDs work, for example openBSD doesn't seem to support this */
|
||||
#define JANET_SPAWN_CHDIR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Not POSIX, but all Unixes but Solaris have this function. */
|
||||
#if defined(JANET_POSIX) && !defined(__sun)
|
||||
time_t timegm(struct tm *tm);
|
||||
@@ -143,8 +117,8 @@ static void janet_unlock_environ(void) {
|
||||
#define janet_stringify(x) janet_stringify1(x)
|
||||
|
||||
JANET_CORE_FN(os_which,
|
||||
"(os/which &opt test)",
|
||||
"Check the current operating system. If `test` is nil or unset, Returns one of:\n\n"
|
||||
"(os/which)",
|
||||
"Check the current operating system. Returns one of:\n\n"
|
||||
"* :windows\n\n"
|
||||
"* :mingw\n\n"
|
||||
"* :cygwin\n\n"
|
||||
@@ -157,12 +131,9 @@ JANET_CORE_FN(os_which,
|
||||
"* :dragonfly\n\n"
|
||||
"* :bsd\n\n"
|
||||
"* :posix - A POSIX compatible system (default)\n\n"
|
||||
"May also return a custom keyword specified at build time. Is `test` is truthy, will check if the current operating system equals `test` and return true if they are the same, false otherwise.") {
|
||||
janet_arity(argc, 0, 1);
|
||||
if (argc == 1 && janet_truthy(argv[0])) {
|
||||
janet_getkeyword(argv, 0); /* Constrain to keywords */
|
||||
return janet_wrap_boolean(janet_equals(argv[0], os_which(0, NULL)));
|
||||
}
|
||||
"May also return a custom keyword specified at build time.") {
|
||||
janet_fixarity(argc, 0);
|
||||
(void) argv;
|
||||
#if defined(JANET_OS_NAME)
|
||||
return janet_ckeywordv(janet_stringify(JANET_OS_NAME));
|
||||
#elif defined(JANET_MINGW)
|
||||
@@ -187,8 +158,6 @@ JANET_CORE_FN(os_which,
|
||||
return janet_ckeywordv("dragonfly");
|
||||
#elif defined(JANET_BSD)
|
||||
return janet_ckeywordv("bsd");
|
||||
#elif defined(JANET_ILLUMOS)
|
||||
return janet_ckeywordv("illumos");
|
||||
#else
|
||||
return janet_ckeywordv("posix");
|
||||
#endif
|
||||
@@ -250,7 +219,6 @@ JANET_CORE_FN(os_compiler,
|
||||
"* :gcc\n\n"
|
||||
"* :clang\n\n"
|
||||
"* :msvc\n\n"
|
||||
"* :kencc\n\n"
|
||||
"* :unknown\n\n") {
|
||||
janet_fixarity(argc, 0);
|
||||
(void) argv;
|
||||
@@ -260,8 +228,6 @@ JANET_CORE_FN(os_compiler,
|
||||
return janet_ckeywordv("clang");
|
||||
#elif defined(__GNUC__)
|
||||
return janet_ckeywordv("gcc");
|
||||
#elif defined(JANET_PLAN9)
|
||||
return janet_ckeywordv("kencc");
|
||||
#else
|
||||
return janet_ckeywordv("unknown");
|
||||
#endif
|
||||
@@ -300,43 +266,39 @@ JANET_CORE_FN(os_cpu_count,
|
||||
"Get an approximate number of CPUs available on for this process to use. If "
|
||||
"unable to get an approximation, will return a default value dflt.") {
|
||||
janet_arity(argc, 0, 1);
|
||||
(void) argv; /* Prevent unused argument warning */
|
||||
Janet dflt = argc > 0 ? argv[0] : janet_wrap_nil();
|
||||
#ifdef JANET_WINDOWS
|
||||
(void) dflt;
|
||||
SYSTEM_INFO info;
|
||||
GetSystemInfo(&info);
|
||||
return janet_wrap_integer(info.dwNumberOfProcessors);
|
||||
#elif defined(JANET_LINUX)
|
||||
(void) dflt;
|
||||
cpu_set_t cs;
|
||||
CPU_ZERO(&cs);
|
||||
sched_getaffinity(0, sizeof(cs), &cs);
|
||||
int count = CPU_COUNT(&cs);
|
||||
return janet_wrap_integer(count);
|
||||
#elif defined(JANET_BSD) && defined(HW_NCPUONLINE)
|
||||
(void) dflt;
|
||||
const int name[2] = {CTL_HW, HW_NCPUONLINE};
|
||||
int result = 0;
|
||||
size_t len = sizeof(int);
|
||||
if (-1 == sysctl(name, 2, &result, &len, NULL, 0)) {
|
||||
return argc > 0 ? argv[0] : janet_wrap_nil();
|
||||
return dflt;
|
||||
}
|
||||
return janet_wrap_integer(result);
|
||||
#elif defined(JANET_BSD) && defined(HW_NCPU)
|
||||
(void) dflt;
|
||||
const int name[2] = {CTL_HW, HW_NCPU};
|
||||
int result = 0;
|
||||
size_t len = sizeof(int);
|
||||
if (-1 == sysctl(name, 2, &result, &len, NULL, 0)) {
|
||||
return argc > 0 ? argv[0] : janet_wrap_nil();
|
||||
return dflt;
|
||||
}
|
||||
return janet_wrap_integer(result);
|
||||
#elif defined(JANET_ILLUMOS)
|
||||
long result = sysconf(_SC_NPROCESSORS_CONF);
|
||||
if (result < 0) {
|
||||
return argc > 0 ? argv[0] : janet_wrap_nil();
|
||||
}
|
||||
return janet_wrap_integer(result);
|
||||
#elif defined(JANET_PLAN9)
|
||||
return janet_wrap_integer(atoi(getenv("NPROC")));
|
||||
#else
|
||||
return argc > 0 ? argv[0] : janet_wrap_nil();
|
||||
return dflt;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -366,8 +328,6 @@ static EnvBlock os_execute_env(int32_t argc, const Janet *argv) {
|
||||
janet_buffer_push_bytes(temp, vals, janet_string_length(vals));
|
||||
janet_buffer_push_u8(temp, '\0');
|
||||
}
|
||||
/* Windows environment blocks must be double-NULL terminated */
|
||||
if (temp->count == 0) janet_buffer_push_u8(temp, '\0');
|
||||
janet_buffer_push_u8(temp, '\0');
|
||||
char *ret = janet_smalloc(temp->count);
|
||||
memcpy(ret, temp->data, temp->count);
|
||||
@@ -581,12 +541,11 @@ static void janet_proc_wait_cb(JanetEVGenericMessage args) {
|
||||
proc->flags &= ~JANET_PROC_WAITING;
|
||||
janet_gcunroot(janet_wrap_abstract(proc));
|
||||
janet_gcunroot(janet_wrap_fiber(args.fiber));
|
||||
uint32_t sched_id = (uint32_t) args.argi;
|
||||
if (janet_fiber_can_resume(args.fiber) && args.fiber->sched_id == sched_id) {
|
||||
if ((status != 0) && (proc->flags & JANET_PROC_ERROR_NONZERO)) {
|
||||
JanetString s = janet_formatc("command failed with non-zero exit code %d", status);
|
||||
janet_cancel(args.fiber, janet_wrap_string(s));
|
||||
} else {
|
||||
if ((status != 0) && (proc->flags & JANET_PROC_ERROR_NONZERO)) {
|
||||
JanetString s = janet_formatc("command failed with non-zero exit code %d", status);
|
||||
janet_cancel(args.fiber, janet_wrap_string(s));
|
||||
} else {
|
||||
if (janet_fiber_can_resume(args.fiber)) {
|
||||
janet_schedule(args.fiber, janet_wrap_integer(status));
|
||||
}
|
||||
}
|
||||
@@ -644,7 +603,6 @@ os_proc_wait_impl(JanetProc *proc) {
|
||||
memset(&targs, 0, sizeof(targs));
|
||||
targs.argp = proc;
|
||||
targs.fiber = janet_root_fiber();
|
||||
targs.argi = (uint32_t) targs.fiber->sched_id;
|
||||
janet_gcroot(janet_wrap_abstract(proc));
|
||||
janet_gcroot(janet_wrap_fiber(targs.fiber));
|
||||
janet_ev_threaded_call(janet_proc_wait_subr, targs, janet_proc_wait_cb);
|
||||
@@ -671,15 +629,16 @@ os_proc_wait_impl(JanetProc *proc) {
|
||||
|
||||
JANET_CORE_FN(os_proc_wait,
|
||||
"(os/proc-wait proc)",
|
||||
"Suspend the current fiber until the subprocess `proc` completes. Once `proc` "
|
||||
"completes, return the exit code of `proc`. If called more than once on the same "
|
||||
"core/process value, will raise an error. When creating subprocesses using "
|
||||
"`os/spawn`, this function should be called on the returned value to avoid zombie "
|
||||
"processes.") {
|
||||
"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
|
||||
os_proc_wait_impl(proc);
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
return os_proc_wait_impl(proc);
|
||||
#endif
|
||||
@@ -784,13 +743,12 @@ 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 the subprocess `proc` by sending SIGKILL to it on POSIX systems, or by closing "
|
||||
"the process handle on Windows. If `proc` has already completed, raise an error. If "
|
||||
"`wait` is truthy, will wait for `proc` to complete and return the exit code (this "
|
||||
"will raise an error if `proc` is being waited for). Otherwise, return `proc`. If "
|
||||
"`signal` is provided, send it instead of SIGKILL. Signal keywords are named after "
|
||||
"their C counterparts but in lowercase with the leading SIG stripped. `signal` is "
|
||||
"ignored on Windows.") {
|
||||
"Kill a subprocess by sending SIGKILL to it on posix systems, or by closing the process "
|
||||
"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) {
|
||||
@@ -818,6 +776,7 @@ JANET_CORE_FN(os_proc_kill,
|
||||
if (argc > 1 && janet_truthy(argv[1])) {
|
||||
#ifdef JANET_EV
|
||||
os_proc_wait_impl(proc);
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
return os_proc_wait_impl(proc);
|
||||
#endif
|
||||
@@ -828,9 +787,9 @@ JANET_CORE_FN(os_proc_kill,
|
||||
|
||||
JANET_CORE_FN(os_proc_close,
|
||||
"(os/proc-close proc)",
|
||||
"Close pipes created for subprocess `proc` by `os/spawn` if they have not been "
|
||||
"closed. Then, if `proc` is not being waited for, wait. If this function waits, when "
|
||||
"`proc` completes, return the exit code of `proc`. Otherwise, return 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
|
||||
@@ -848,24 +807,12 @@ JANET_CORE_FN(os_proc_close,
|
||||
}
|
||||
#ifdef JANET_EV
|
||||
os_proc_wait_impl(proc);
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
return os_proc_wait_impl(proc);
|
||||
#endif
|
||||
}
|
||||
|
||||
JANET_CORE_FN(os_proc_getpid,
|
||||
"(os/getpid)",
|
||||
"Get the process ID of the current process.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS);
|
||||
janet_fixarity(argc, 0);
|
||||
(void) argv;
|
||||
#ifdef JANET_WINDOWS
|
||||
return janet_wrap_number((double) _getpid());
|
||||
#else
|
||||
return janet_wrap_number((double) getpid());
|
||||
#endif
|
||||
}
|
||||
|
||||
static void swap_handles(JanetHandle *handles) {
|
||||
JanetHandle temp = handles[0];
|
||||
handles[0] = handles[1];
|
||||
@@ -1190,7 +1137,6 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
JanetAbstract orig_in = NULL, orig_out = NULL, orig_err = NULL;
|
||||
JanetHandle new_in = JANET_HANDLE_NONE, new_out = JANET_HANDLE_NONE, new_err = JANET_HANDLE_NONE;
|
||||
JanetHandle pipe_in = JANET_HANDLE_NONE, pipe_out = JANET_HANDLE_NONE, pipe_err = JANET_HANDLE_NONE;
|
||||
int stderr_is_stdout = 0;
|
||||
int pipe_errflag = 0; /* Track errors setting up pipes */
|
||||
int pipe_owner_flags = (is_spawn && (flags & 0x8)) ? JANET_PROC_ALLOW_ZOMBIE : 0;
|
||||
|
||||
@@ -1215,28 +1161,11 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
if (is_spawn && janet_keyeq(maybe_stderr, "pipe")) {
|
||||
new_err = make_pipes(&pipe_err, 0, &pipe_errflag);
|
||||
pipe_owner_flags |= JANET_PROC_OWNS_STDERR;
|
||||
} else if (janet_keyeq(maybe_stderr, "out")) {
|
||||
stderr_is_stdout = 1;
|
||||
} else if (!janet_checktype(maybe_stderr, JANET_NIL)) {
|
||||
new_err = janet_getjstream(&maybe_stderr, 0, &orig_err);
|
||||
}
|
||||
}
|
||||
|
||||
/* Optional working directory. Available for both os/execute and os/spawn. */
|
||||
const char *chdir_path = NULL;
|
||||
if (argc > 2) {
|
||||
JanetDictView tab = janet_getdictionary(argv, 2);
|
||||
Janet workdir = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("cd"));
|
||||
if (janet_checktype(workdir, JANET_STRING)) {
|
||||
chdir_path = (const char *) janet_unwrap_string(workdir);
|
||||
#ifndef JANET_SPAWN_CHDIR
|
||||
janet_panicf(":cd argument not supported on this system - %s", chdir_path);
|
||||
#endif
|
||||
} else if (!janet_checktype(workdir, JANET_NIL)) {
|
||||
janet_panicf("expected string for :cd argumnet, got %v", workdir);
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean up if any of the pipes have any issues */
|
||||
if (pipe_errflag) {
|
||||
if (pipe_in != JANET_HANDLE_NONE) close_handle(pipe_in);
|
||||
@@ -1251,7 +1180,6 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
PROCESS_INFORMATION processInfo;
|
||||
STARTUPINFO startupInfo;
|
||||
LPCSTR lpCurrentDirectory = NULL;
|
||||
memset(&saAttr, 0, sizeof(saAttr));
|
||||
memset(&processInfo, 0, sizeof(processInfo));
|
||||
memset(&startupInfo, 0, sizeof(startupInfo));
|
||||
@@ -1268,10 +1196,6 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
}
|
||||
const char *path = (const char *) janet_unwrap_string(exargs.items[0]);
|
||||
|
||||
if (chdir_path != NULL) {
|
||||
lpCurrentDirectory = chdir_path;
|
||||
}
|
||||
|
||||
/* Do IO redirection */
|
||||
|
||||
if (pipe_in != JANET_HANDLE_NONE) {
|
||||
@@ -1279,7 +1203,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
} else if (new_in != JANET_HANDLE_NONE) {
|
||||
startupInfo.hStdInput = new_in;
|
||||
} else {
|
||||
startupInfo.hStdInput = (HANDLE) _get_osfhandle(_fileno(stdin));
|
||||
startupInfo.hStdInput = (HANDLE) _get_osfhandle(0);
|
||||
}
|
||||
|
||||
if (pipe_out != JANET_HANDLE_NONE) {
|
||||
@@ -1287,21 +1211,18 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
} else if (new_out != JANET_HANDLE_NONE) {
|
||||
startupInfo.hStdOutput = new_out;
|
||||
} else {
|
||||
startupInfo.hStdOutput = (HANDLE) _get_osfhandle(_fileno(stdout));
|
||||
startupInfo.hStdOutput = (HANDLE) _get_osfhandle(1);
|
||||
}
|
||||
|
||||
if (pipe_err != JANET_HANDLE_NONE) {
|
||||
startupInfo.hStdError = pipe_err;
|
||||
} else if (new_err != NULL) {
|
||||
startupInfo.hStdError = new_err;
|
||||
} else if (stderr_is_stdout) {
|
||||
startupInfo.hStdError = startupInfo.hStdOutput;
|
||||
} else {
|
||||
startupInfo.hStdError = (HANDLE) _get_osfhandle(_fileno(stderr));
|
||||
startupInfo.hStdError = (HANDLE) _get_osfhandle(2);
|
||||
}
|
||||
|
||||
int cp_failed = 0;
|
||||
DWORD cp_error_code = 0;
|
||||
if (!CreateProcess(janet_flag_at(flags, 1) ? NULL : path,
|
||||
(char *) buf->data, /* Single CLI argument */
|
||||
&saAttr, /* no proc inheritance */
|
||||
@@ -1309,11 +1230,10 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
TRUE, /* handle inheritance */
|
||||
0, /* flags */
|
||||
use_environ ? NULL : envp, /* pass in environment */
|
||||
lpCurrentDirectory,
|
||||
NULL, /* use parents starting directory */
|
||||
&startupInfo,
|
||||
&processInfo)) {
|
||||
cp_failed = 1;
|
||||
cp_error_code = GetLastError();
|
||||
}
|
||||
|
||||
if (pipe_in != JANET_HANDLE_NONE) CloseHandle(pipe_in);
|
||||
@@ -1323,25 +1243,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
os_execute_cleanup(envp, NULL);
|
||||
|
||||
if (cp_failed) {
|
||||
char msgbuf[256];
|
||||
msgbuf[0] = '\0';
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
cp_error_code,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
msgbuf,
|
||||
sizeof(msgbuf),
|
||||
NULL);
|
||||
if (!*msgbuf) snprintf(msgbuf, sizeof(msgbuf), "%" PRIu32, (uint32_t) cp_error_code);
|
||||
char *c = msgbuf;
|
||||
while (*c) {
|
||||
if (*c == '\n' || *c == '\r') {
|
||||
*c = '\0';
|
||||
break;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
janet_panicf("failed to create process: %s", janet_cstringv(msgbuf));
|
||||
janet_panic("failed to create process");
|
||||
}
|
||||
|
||||
pHandle = processInfo.hProcess;
|
||||
@@ -1366,10 +1268,10 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
|
||||
/* exec mode */
|
||||
if (mode == JANET_EXECUTE_EXEC) {
|
||||
int status;
|
||||
#ifdef JANET_PLAN9
|
||||
status = exec(cargv[0], cargv);
|
||||
#ifdef JANET_WINDOWS
|
||||
janet_panic("not supported on windows");
|
||||
#else
|
||||
int status;
|
||||
if (!use_environ) {
|
||||
environ = envp;
|
||||
}
|
||||
@@ -1380,25 +1282,15 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
status = execv(cargv[0], cargv);
|
||||
}
|
||||
} while (status == -1 && errno == EINTR);
|
||||
#endif
|
||||
janet_panicf("%p: %s", cargv[0], janet_strerror(errno ? errno : ENOENT));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef JANET_NO_SPAWN
|
||||
/* Use posix_spawn to spawn new process */
|
||||
|
||||
/* Posix spawn setup */
|
||||
posix_spawn_file_actions_t actions;
|
||||
posix_spawn_file_actions_init(&actions);
|
||||
#ifdef JANET_SPAWN_CHDIR
|
||||
if (chdir_path != NULL) {
|
||||
#ifdef JANET_SPAWN_CHDIR_NO_NP
|
||||
posix_spawn_file_actions_addchdir(&actions, chdir_path);
|
||||
#else
|
||||
posix_spawn_file_actions_addchdir_np(&actions, chdir_path);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (pipe_in != JANET_HANDLE_NONE) {
|
||||
posix_spawn_file_actions_adddup2(&actions, pipe_in, 0);
|
||||
posix_spawn_file_actions_addclose(&actions, pipe_in);
|
||||
@@ -1421,8 +1313,6 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
} else if (new_err != JANET_HANDLE_NONE && new_err != 2) {
|
||||
posix_spawn_file_actions_adddup2(&actions, new_err, 2);
|
||||
posix_spawn_file_actions_addclose(&actions, new_err);
|
||||
} else if (stderr_is_stdout) {
|
||||
posix_spawn_file_actions_adddup2(&actions, 1, 2);
|
||||
}
|
||||
|
||||
pid_t pid;
|
||||
@@ -1453,8 +1343,6 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#ifndef JANET_NO_SPAWN
|
||||
JanetProc *proc = janet_abstract(&ProcAT, sizeof(JanetProc));
|
||||
proc->return_code = -1;
|
||||
#ifdef JANET_WINDOWS
|
||||
@@ -1492,82 +1380,65 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
return os_proc_wait_impl(proc);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JANET_CORE_FN(os_execute,
|
||||
"(os/execute args &opt flags env)",
|
||||
"Execute a program on the system and return the exit code. `args` is an array/tuple "
|
||||
"of strings. The first string is the name of the program and the remainder are "
|
||||
"arguments passed to the program. `flags` is a keyword made from the following "
|
||||
"characters that modifies how the program executes:\n"
|
||||
"* :e - enables passing an environment to the program. Without 'e', the "
|
||||
"Execute a program on the system and pass it string arguments. `flags` "
|
||||
"is a keyword that modifies how the program will execute.\n"
|
||||
"* :e - enables passing an environment to the program. Without :e, the "
|
||||
"current environment is inherited.\n"
|
||||
"* :p - allows searching the current PATH for the program to execute. "
|
||||
"Without this flag, the first element of `args` must be an absolute path.\n"
|
||||
"* :x - raises error if exit code is non-zero.\n"
|
||||
"* :d - prevents the garbage collector terminating the program (if still running) "
|
||||
"and calling the equivalent of `os/proc-wait` (allows zombie processes).\n"
|
||||
"`env` is a table/struct mapping environment variables to values. It can also "
|
||||
"contain the keys :in, :out, and :err, which allow redirecting stdio in the "
|
||||
"subprocess. :in, :out, and :err should be core/file or core/stream values. "
|
||||
"If core/stream values are used, the caller is responsible for ensuring pipes do not "
|
||||
"cause the program to block and deadlock.") {
|
||||
"* :p - allows searching the current PATH for the binary to execute. "
|
||||
"Without this flag, binaries must use absolute paths.\n"
|
||||
"* :x - raise error if exit code is non-zero.\n"
|
||||
"* :d - Don't try and terminate the process on garbage collection (allow spawning zombies).\n"
|
||||
"`env` is a table or struct mapping environment variables to values. It can also "
|
||||
"contain the keys :in, :out, and :err, which allow redirecting stdio in the subprocess. "
|
||||
":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 core/process value representing the "
|
||||
"spawned subprocess. Takes the same arguments as `os/execute` but does not wait for "
|
||||
"the subprocess to complete. Unlike `os/execute`, the value `:pipe` can be used for "
|
||||
":in, :out and :err keys in `env`. If used, the returned core/process will have a "
|
||||
"writable stream in the :in field and readable streams in the :out and :err fields. "
|
||||
"On non-Windows systems, the subprocess PID will be in the :pid field. The caller is "
|
||||
"responsible for waiting on the process (e.g. by calling `os/proc-wait` on the "
|
||||
"returned core/process value) to avoid creating zombie process. After the subprocess "
|
||||
"completes, the exit value is in the :return-code field. If `flags` includes 'x', a "
|
||||
"non-zero exit code will cause a waiting fiber to raise an error. The use of "
|
||||
"`:pipe` may fail if there are too many active file descriptors. The caller is "
|
||||
"responsible for closing pipes created by `:pipe` (either individually or using "
|
||||
"`os/proc-close`). Similar to `os/execute`, the caller is responsible for ensuring "
|
||||
"pipes do not cause the program to block and deadlock. As a special case, the stream passed to `:err` "
|
||||
"can be the keyword `:out` to redirect stderr to stdout in the subprocess.") {
|
||||
"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 "
|
||||
"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. "
|
||||
"However, instead of creating a subprocess, the current process is replaced. Is not supported on Windows, and "
|
||||
"However, instead of creating a subprocess, the current process is replaced. Is not supported on windows, and "
|
||||
"does not allow redirection of stdio.") {
|
||||
#ifdef JANET_WINDOWS
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
janet_panic("not supported on Windows");
|
||||
#else
|
||||
return os_execute_impl(argc, argv, JANET_EXECUTE_EXEC);
|
||||
#endif
|
||||
}
|
||||
|
||||
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 and Plan 9 only).") {
|
||||
"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 on Windows");
|
||||
janet_panic("not supported");
|
||||
#else
|
||||
pid_t result;
|
||||
#ifdef JANET_PLAN9
|
||||
result = fork();
|
||||
#else
|
||||
do {
|
||||
result = fork();
|
||||
} while (result == -1 && errno == EINTR);
|
||||
#endif
|
||||
if (result == -1) {
|
||||
janet_panic(janet_strerror(errno));
|
||||
}
|
||||
@@ -1582,28 +1453,6 @@ JANET_CORE_FN(os_posix_fork,
|
||||
#endif
|
||||
}
|
||||
|
||||
JANET_CORE_FN(os_posix_chroot,
|
||||
"(os/posix-chroot dirname)",
|
||||
"Call `chroot` to change the root directory to `dirname`. "
|
||||
"Not supported on all systems (POSIX only).") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_CHROOT);
|
||||
janet_fixarity(argc, 1);
|
||||
#if defined(JANET_WINDOWS) || defined(JANET_PLAN9)
|
||||
(void) argv;
|
||||
janet_panic("not supported on Windows or Plan 9");
|
||||
#else
|
||||
const char *root = janet_getcstring(argv, 0);
|
||||
int result;
|
||||
do {
|
||||
result = chroot(root);
|
||||
} while (result == -1 && errno == EINTR);
|
||||
if (result == -1) {
|
||||
janet_panic(janet_strerror(errno));
|
||||
}
|
||||
return janet_wrap_nil();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JANET_EV
|
||||
/* Runs in a separate thread */
|
||||
static JanetEVGenericMessage os_shell_subr(JanetEVGenericMessage args) {
|
||||
@@ -1639,7 +1488,6 @@ JANET_CORE_FN(os_shell,
|
||||
|
||||
#endif /* JANET_NO_PROCESSES */
|
||||
|
||||
#ifndef JANET_PLAN9
|
||||
JANET_CORE_FN(os_environ,
|
||||
"(os/environ)",
|
||||
"Get a copy of the OS environment table.") {
|
||||
@@ -1671,7 +1519,6 @@ JANET_CORE_FN(os_environ,
|
||||
janet_unlock_environ();
|
||||
return janet_wrap_table(t);
|
||||
}
|
||||
#endif
|
||||
|
||||
JANET_CORE_FN(os_getenv,
|
||||
"(os/getenv variable &opt dflt)",
|
||||
@@ -1696,9 +1543,6 @@ JANET_CORE_FN(os_setenv,
|
||||
#ifdef JANET_WINDOWS
|
||||
#define SETENV(K,V) _putenv_s(K, V)
|
||||
#define UNSETENV(K) _putenv_s(K, "")
|
||||
#elif defined(JANET_PLAN9)
|
||||
#define SETENV(K,V) putenv(K, V)
|
||||
#define UNSETENV(K) unsetenv(K)
|
||||
#else
|
||||
#define SETENV(K,V) setenv(K, V, 1)
|
||||
#define UNSETENV(K) unsetenv(K)
|
||||
@@ -1871,8 +1715,6 @@ static struct tm *time_to_tm(const Janet *argv, int32_t argc, int32_t n, struct
|
||||
_tzset();
|
||||
localtime_s(t_infos, &t);
|
||||
t_info = t_infos;
|
||||
#elif defined(JANET_PLAN9)
|
||||
t_info = localtime(&t);
|
||||
#else
|
||||
tzset();
|
||||
t_info = localtime_r(&t, t_infos);
|
||||
@@ -1882,8 +1724,6 @@ static struct tm *time_to_tm(const Janet *argv, int32_t argc, int32_t n, struct
|
||||
#ifdef JANET_WINDOWS
|
||||
gmtime_s(t_infos, &t);
|
||||
t_info = t_infos;
|
||||
#elif defined(JANET_PLAN9)
|
||||
t_info = gmtime(&t);
|
||||
#else
|
||||
t_info = gmtime_r(&t, t_infos);
|
||||
#endif
|
||||
@@ -1894,8 +1734,9 @@ static struct tm *time_to_tm(const Janet *argv, int32_t argc, int32_t n, struct
|
||||
JANET_CORE_FN(os_date,
|
||||
"(os/date &opt time local)",
|
||||
"Returns the given time as a date struct, or the current time if `time` is not given. "
|
||||
"Returns a struct with following key values. Note that all numbers are 0-indexed. "
|
||||
"Date is given in UTC unless `local` is truthy, in which case the date is formatted for "
|
||||
"the local timezone. Returns a struct with following key values. Note that all numbers are 0-indexed.\n\n"
|
||||
"the local timezone.\n\n"
|
||||
"* :seconds - number of seconds [0-61]\n\n"
|
||||
"* :minutes - number of minutes [0-59]\n\n"
|
||||
"* :hours - number of hours [0-23]\n\n"
|
||||
@@ -1904,9 +1745,7 @@ JANET_CORE_FN(os_date,
|
||||
"* :year - years since year 0 (e.g. 2019)\n\n"
|
||||
"* :week-day - day of the week [0-6]\n\n"
|
||||
"* :year-day - day of the year [0-365]\n\n"
|
||||
"* :dst - if Day Light Savings is in effect\n\n"
|
||||
"You can set local timezone by setting TZ environment variable. "
|
||||
"See tzset(<time.h>) or _tzset(<time.h>) for further details.") {
|
||||
"* :dst - if Day Light Savings is in effect") {
|
||||
janet_arity(argc, 0, 2);
|
||||
(void) argv;
|
||||
struct tm t_infos;
|
||||
@@ -1924,15 +1763,14 @@ JANET_CORE_FN(os_date,
|
||||
return janet_wrap_struct(janet_struct_end(st));
|
||||
}
|
||||
|
||||
#define SIZETIMEFMT 250
|
||||
#define SIZETIMEFMT 250
|
||||
|
||||
JANET_CORE_FN(os_strftime,
|
||||
"(os/strftime fmt &opt time local)",
|
||||
"Format the given time as a string, or the current time if `time` is not given. "
|
||||
"The time is formatted according to the same rules as the ISO C89 function strftime(). "
|
||||
"The time is formatted in UTC unless `local` is truthy, in which case the date is formatted for "
|
||||
"the local timezone. You can set local timezone by setting TZ environment variable. "
|
||||
"See tzset(<time.h>) or _tzset(<time.h>) for further details.") {
|
||||
"the local timezone.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
const char *fmt = janet_getcstring(argv, 0);
|
||||
/* ANSI X3.159-1989, section 4.12.3.5 "The strftime function" */
|
||||
@@ -1940,9 +1778,6 @@ JANET_CORE_FN(os_strftime,
|
||||
const char *p = fmt;
|
||||
while (*p) {
|
||||
if (*p++ == '%') {
|
||||
if (!*p) {
|
||||
janet_panic("invalid conversion specifier");
|
||||
}
|
||||
if (!strchr(valid, *p)) {
|
||||
janet_panicf("invalid conversion specifier '%%%c'", *p);
|
||||
}
|
||||
@@ -1952,7 +1787,7 @@ JANET_CORE_FN(os_strftime,
|
||||
struct tm t_infos;
|
||||
struct tm *t_info = time_to_tm(argv, argc, 1, &t_infos);
|
||||
char buf[SIZETIMEFMT];
|
||||
(void)strftime(buf, sizeof(buf), fmt, t_info);
|
||||
(void)strftime(buf, SIZETIMEFMT, fmt, t_info);
|
||||
return janet_cstringv(buf);
|
||||
}
|
||||
|
||||
@@ -1960,7 +1795,7 @@ static int entry_getdst(Janet env_entry) {
|
||||
Janet v;
|
||||
if (janet_checktype(env_entry, JANET_TABLE)) {
|
||||
JanetTable *entry = janet_unwrap_table(env_entry);
|
||||
v = janet_table_get_keyword(entry, "dst");
|
||||
v = janet_table_get(entry, janet_ckeywordv("dst"));
|
||||
} else if (janet_checktype(env_entry, JANET_STRUCT)) {
|
||||
const JanetKV *entry = janet_unwrap_struct(env_entry);
|
||||
v = janet_struct_get(entry, janet_ckeywordv("dst"));
|
||||
@@ -1984,7 +1819,7 @@ static timeint_t entry_getint(Janet env_entry, char *field) {
|
||||
Janet i;
|
||||
if (janet_checktype(env_entry, JANET_TABLE)) {
|
||||
JanetTable *entry = janet_unwrap_table(env_entry);
|
||||
i = janet_table_get_keyword(entry, field);
|
||||
i = janet_table_get(entry, janet_ckeywordv(field));
|
||||
} else if (janet_checktype(env_entry, JANET_STRUCT)) {
|
||||
const JanetKV *entry = janet_unwrap_struct(env_entry);
|
||||
i = janet_struct_get(entry, janet_ckeywordv(field));
|
||||
@@ -2045,6 +1880,7 @@ JANET_CORE_FN(os_mktime,
|
||||
/* utc time */
|
||||
#ifdef JANET_NO_UTC_MKTIME
|
||||
janet_panic("os/mktime UTC not supported on this platform");
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
t = timegm(&t_info);
|
||||
#endif
|
||||
@@ -2063,7 +1899,6 @@ JANET_CORE_FN(os_mktime,
|
||||
#define j_symlink symlink
|
||||
#endif
|
||||
|
||||
#ifndef JANET_NO_LOCALES
|
||||
JANET_CORE_FN(os_setlocale,
|
||||
"(os/setlocale &opt locale category)",
|
||||
"Set the system locale, which affects how dates and numbers are formatted. "
|
||||
@@ -2100,20 +1935,20 @@ JANET_CORE_FN(os_setlocale,
|
||||
if (old == NULL) return janet_wrap_nil();
|
||||
return janet_cstringv(old);
|
||||
}
|
||||
#endif
|
||||
|
||||
JANET_CORE_FN(os_link,
|
||||
"(os/link oldpath newpath &opt symlink)",
|
||||
"Create a link at newpath that points to oldpath and returns nil. "
|
||||
"Iff symlink is truthy, creates a symlink. "
|
||||
"Iff symlink is falsey or not provided, "
|
||||
"creates a hard link. Does not work on Windows or Plan 9.") {
|
||||
"creates a hard link. Does not work on Windows.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
|
||||
janet_arity(argc, 2, 3);
|
||||
#if defined(JANET_WINDOWS) || defined(JANET_PLAN9)
|
||||
#ifdef JANET_WINDOWS
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
janet_panic("not supported on Windows or Plan 9");
|
||||
janet_panic("os/link not supported on Windows");
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
const char *oldpath = janet_getcstring(argv, 0);
|
||||
const char *newpath = janet_getcstring(argv, 1);
|
||||
@@ -2128,10 +1963,11 @@ JANET_CORE_FN(os_symlink,
|
||||
"Create a symlink from oldpath to newpath, returning nil. Same as `(os/link oldpath newpath true)`.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
|
||||
janet_fixarity(argc, 2);
|
||||
#if defined(JANET_WINDOWS) || defined(JANET_PLAN9)
|
||||
#ifdef JANET_WINDOWS
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
janet_panic("not supported on Windows or Plan 9");
|
||||
janet_panic("os/symlink not supported on Windows");
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
const char *oldpath = janet_getcstring(argv, 0);
|
||||
const char *newpath = janet_getcstring(argv, 1);
|
||||
@@ -2169,8 +2005,6 @@ JANET_CORE_FN(os_rmdir,
|
||||
const char *path = janet_getcstring(argv, 0);
|
||||
#ifdef JANET_WINDOWS
|
||||
int res = _rmdir(path);
|
||||
#elif defined(JANET_PLAN9)
|
||||
int res = remove(path);
|
||||
#else
|
||||
int res = rmdir(path);
|
||||
#endif
|
||||
@@ -2235,7 +2069,8 @@ JANET_CORE_FN(os_readlink,
|
||||
#ifdef JANET_WINDOWS
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
janet_panic("not supported on Windows");
|
||||
janet_panic("os/readlink not supported on Windows");
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
static char buffer[PATH_MAX];
|
||||
const char *path = janet_getcstring(argv, 0);
|
||||
@@ -2297,13 +2132,11 @@ static const uint8_t *janet_decode_mode(mode_t m) {
|
||||
const char *str = "other";
|
||||
if (S_ISREG(m)) str = "file";
|
||||
else if (S_ISDIR(m)) str = "directory";
|
||||
#ifndef JANET_PLAN9
|
||||
else if (S_ISFIFO(m)) str = "fifo";
|
||||
else if (S_ISBLK(m)) str = "block";
|
||||
else if (S_ISSOCK(m)) str = "socket";
|
||||
else if (S_ISLNK(m)) str = "link";
|
||||
else if (S_ISCHR(m)) str = "character";
|
||||
#endif
|
||||
return janet_ckeyword(str);
|
||||
}
|
||||
|
||||
@@ -2468,9 +2301,6 @@ static Janet os_stat_or_lstat(int do_lstat, int32_t argc, Janet *argv) {
|
||||
#ifdef JANET_WINDOWS
|
||||
(void) do_lstat;
|
||||
int res = _stat(path, &st);
|
||||
#elif defined(JANET_PLAN9)
|
||||
(void)do_lstat;
|
||||
int res = stat(path, &st);
|
||||
#else
|
||||
int res;
|
||||
if (do_lstat) {
|
||||
@@ -2496,6 +2326,7 @@ static Janet os_stat_or_lstat(int do_lstat, int32_t argc, Janet *argv) {
|
||||
return sg->fn(&st);
|
||||
}
|
||||
janet_panicf("unexpected keyword %v", janet_wrap_keyword(key));
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2531,13 +2362,9 @@ JANET_CORE_FN(os_chmod,
|
||||
"Change file permissions, where `mode` is a permission string as returned by "
|
||||
"`os/perm-string`, or an integer as returned by `os/perm-int`. "
|
||||
"When `mode` is an integer, it is interpreted as a Unix permission value, best specified in octal, like "
|
||||
"8r666 or 8r400. Windows will not differentiate between user, group, and other permissions, and thus will combine all of these permissions. Returns nil."
|
||||
"Unsupported on plan9.") {
|
||||
"8r666 or 8r400. Windows will not differentiate between user, group, and other permissions, and thus will combine all of these permissions. Returns nil.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
|
||||
janet_fixarity(argc, 2);
|
||||
#ifdef JANET_PLAN9
|
||||
janet_panic("not supported on Plan 9");
|
||||
#else
|
||||
const char *path = janet_getcstring(argv, 0);
|
||||
#ifdef JANET_WINDOWS
|
||||
int res = _chmod(path, os_getmode(argv, 1));
|
||||
@@ -2546,7 +2373,6 @@ JANET_CORE_FN(os_chmod,
|
||||
#endif
|
||||
if (-1 == res) janet_panicf("%s: %s", janet_strerror(errno), path);
|
||||
return janet_wrap_nil();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef JANET_NO_UMASK
|
||||
@@ -2579,7 +2405,7 @@ JANET_CORE_FN(os_dir,
|
||||
char pattern[MAX_PATH + 1];
|
||||
if (strlen(dir) > (sizeof(pattern) - 3))
|
||||
janet_panicf("path too long: %s", dir);
|
||||
snprintf(pattern, sizeof(pattern), "%s/*", dir);
|
||||
sprintf(pattern, "%s/*", dir);
|
||||
intptr_t res = _findfirst(pattern, &afile);
|
||||
if (-1 == res) janet_panicv(janet_cstringv(janet_strerror(errno)));
|
||||
do {
|
||||
@@ -2645,15 +2471,7 @@ JANET_CORE_FN(os_realpath,
|
||||
#endif
|
||||
if (NULL == dest) janet_panicf("%s: %s", janet_strerror(errno), src);
|
||||
Janet ret = janet_cstringv(dest);
|
||||
#ifdef JANET_WINDOWS
|
||||
DWORD attrib = GetFileAttributes(dest);
|
||||
free(dest); /* if janet_malloc is redefined, still use free to correspond with _fullpath */
|
||||
if (attrib == INVALID_FILE_ATTRIBUTES) {
|
||||
janet_panicf("path does not exist: %v", ret);
|
||||
}
|
||||
#else
|
||||
janet_free(dest);
|
||||
#endif
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
@@ -2697,11 +2515,10 @@ JANET_CORE_FN(os_open,
|
||||
" * :c - create a new file (O\\_CREATE)\n"
|
||||
" * :e - fail if the file exists (O\\_EXCL)\n"
|
||||
" * :t - shorten an existing file to length 0 (O\\_TRUNC)\n\n"
|
||||
" * :a - append to a file (O\\_APPEND on posix, FILE_APPEND_DATA on windows)\n"
|
||||
"Posix-only flags:\n\n"
|
||||
" * :a - append to a file (O\\_APPEND)\n"
|
||||
" * :x - O\\_SYNC\n"
|
||||
" * :C - O\\_NOCTTY\n\n"
|
||||
" * :N - Turn off O\\_NONBLOCK and disable ev reading/writing\n\n"
|
||||
"Windows-only flags:\n\n"
|
||||
" * :R - share reads (FILE\\_SHARE\\_READ)\n"
|
||||
" * :W - share writes (FILE\\_SHARE\\_WRITE)\n"
|
||||
@@ -2711,24 +2528,19 @@ JANET_CORE_FN(os_open,
|
||||
" * :F - FILE\\_ATTRIBUTE\\_OFFLINE\n"
|
||||
" * :T - FILE\\_ATTRIBUTE\\_TEMPORARY\n"
|
||||
" * :d - FILE\\_FLAG\\_DELETE\\_ON\\_CLOSE\n"
|
||||
" * :V - Turn off FILE\\_FLAG\\_OVERLAPPED and disable ev reading/writing\n"
|
||||
" * :I - set bInheritHandle on the created file so it can be passed to other processes.\n"
|
||||
" * :b - FILE\\_FLAG\\_NO\\_BUFFERING\n") {
|
||||
janet_arity(argc, 1, 3);
|
||||
const char *path = janet_getcstring(argv, 0);
|
||||
const uint8_t *opt_flags = janet_optkeyword(argv, argc, 1, (const uint8_t *) "r");
|
||||
jmode_t mode = os_optmode(argc, argv, 2, 0666);
|
||||
uint32_t stream_flags = 0;
|
||||
int disable_stream_mode = 0;
|
||||
JanetHandle fd;
|
||||
#ifdef JANET_WINDOWS
|
||||
(void) mode;
|
||||
int inherited_handle = 0;
|
||||
DWORD desiredAccess = 0;
|
||||
DWORD shareMode = 0;
|
||||
DWORD creationDisp = 0;
|
||||
DWORD fileFlags = FILE_FLAG_OVERLAPPED;
|
||||
DWORD fileAttributes = 0;
|
||||
DWORD flagsAndAttributes = FILE_FLAG_OVERLAPPED;
|
||||
/* We map unix-like open flags to the creationDisp parameter */
|
||||
int creatUnix = 0;
|
||||
#define OCREAT 1
|
||||
@@ -2748,11 +2560,6 @@ JANET_CORE_FN(os_open,
|
||||
stream_flags |= JANET_STREAM_WRITABLE;
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
|
||||
break;
|
||||
case 'a':
|
||||
desiredAccess |= FILE_APPEND_DATA;
|
||||
stream_flags |= JANET_STREAM_WRITABLE;
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
|
||||
break;
|
||||
case 'c':
|
||||
creatUnix |= OCREAT;
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
|
||||
@@ -2775,29 +2582,22 @@ JANET_CORE_FN(os_open,
|
||||
shareMode |= FILE_SHARE_WRITE;
|
||||
break;
|
||||
case 'H':
|
||||
fileAttributes |= FILE_ATTRIBUTE_HIDDEN;
|
||||
flagsAndAttributes |= FILE_ATTRIBUTE_HIDDEN;
|
||||
break;
|
||||
case 'O':
|
||||
fileAttributes |= FILE_ATTRIBUTE_READONLY;
|
||||
flagsAndAttributes |= FILE_ATTRIBUTE_READONLY;
|
||||
break;
|
||||
case 'F':
|
||||
fileAttributes |= FILE_ATTRIBUTE_OFFLINE;
|
||||
flagsAndAttributes |= FILE_ATTRIBUTE_OFFLINE;
|
||||
break;
|
||||
case 'T':
|
||||
fileAttributes |= FILE_ATTRIBUTE_TEMPORARY;
|
||||
flagsAndAttributes |= FILE_ATTRIBUTE_TEMPORARY;
|
||||
break;
|
||||
case 'd':
|
||||
fileFlags |= FILE_FLAG_DELETE_ON_CLOSE;
|
||||
flagsAndAttributes |= FILE_FLAG_DELETE_ON_CLOSE;
|
||||
break;
|
||||
case 'b':
|
||||
fileFlags |= FILE_FLAG_NO_BUFFERING;
|
||||
break;
|
||||
case 'I':
|
||||
inherited_handle = 1;
|
||||
break;
|
||||
case 'V':
|
||||
fileFlags &= ~FILE_FLAG_OVERLAPPED;
|
||||
disable_stream_mode = 1;
|
||||
flagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
|
||||
break;
|
||||
/* we could potentially add more here -
|
||||
* https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
|
||||
@@ -2823,16 +2623,7 @@ JANET_CORE_FN(os_open,
|
||||
creationDisp = TRUNCATE_EXISTING;
|
||||
break;
|
||||
}
|
||||
if (fileAttributes == 0) {
|
||||
fileAttributes = FILE_ATTRIBUTE_NORMAL;
|
||||
}
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
memset(&saAttr, 0, sizeof(saAttr));
|
||||
saAttr.nLength = sizeof(saAttr);
|
||||
if (inherited_handle) {
|
||||
saAttr.bInheritHandle = TRUE; /* Needed to do interesting things with file */
|
||||
}
|
||||
fd = CreateFileA(path, desiredAccess, shareMode, &saAttr, creationDisp, fileFlags | fileAttributes, NULL);
|
||||
fd = CreateFileA(path, desiredAccess, shareMode, NULL, creationDisp, flagsAndAttributes, NULL);
|
||||
if (fd == INVALID_HANDLE_VALUE) janet_panicv(janet_ev_lasterr());
|
||||
#else
|
||||
int open_flags = O_NONBLOCK;
|
||||
@@ -2876,10 +2667,6 @@ JANET_CORE_FN(os_open,
|
||||
case 'a':
|
||||
open_flags |= O_APPEND;
|
||||
break;
|
||||
case 'N':
|
||||
open_flags &= ~O_NONBLOCK;
|
||||
disable_stream_mode = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* If both read and write, fix up to O_RDWR */
|
||||
@@ -2896,7 +2683,7 @@ JANET_CORE_FN(os_open,
|
||||
} while (fd == -1 && errno == EINTR);
|
||||
if (fd == -1) janet_panicv(janet_ev_lasterr());
|
||||
#endif
|
||||
return janet_wrap_abstract(janet_stream(fd, disable_stream_mode ? 0 : stream_flags, NULL));
|
||||
return janet_wrap_abstract(janet_stream(fd, stream_flags, NULL));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(os_pipe,
|
||||
@@ -2964,14 +2751,10 @@ 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),
|
||||
#ifndef JANET_NO_LOCALES
|
||||
JANET_CORE_REG("os/setlocale", os_setlocale),
|
||||
#endif
|
||||
|
||||
/* env functions */
|
||||
#ifndef JANET_PLAN9
|
||||
JANET_CORE_REG("os/environ", os_environ),
|
||||
#endif
|
||||
JANET_CORE_REG("os/getenv", os_getenv),
|
||||
JANET_CORE_REG("os/setenv", os_setenv),
|
||||
|
||||
@@ -3007,16 +2790,11 @@ void janet_lib_os(JanetTable *env) {
|
||||
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),
|
||||
JANET_CORE_REG("os/posix-chroot", os_posix_chroot),
|
||||
/* no need to sandbox process management if you can't create processes
|
||||
* (allows for limited functionality if use exposes C-functions to create specific processes) */
|
||||
JANET_CORE_REG("os/proc-wait", os_proc_wait),
|
||||
JANET_CORE_REG("os/proc-kill", os_proc_kill),
|
||||
JANET_CORE_REG("os/proc-close", os_proc_close),
|
||||
JANET_CORE_REG("os/getpid", os_proc_getpid),
|
||||
#ifdef JANET_EV
|
||||
JANET_CORE_REG("os/sigaction", os_sigaction),
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* high resolution timers */
|
||||
@@ -3025,6 +2803,7 @@ void janet_lib_os(JanetTable *env) {
|
||||
#ifdef JANET_EV
|
||||
JANET_CORE_REG("os/open", os_open), /* fs read and write */
|
||||
JANET_CORE_REG("os/pipe", os_pipe),
|
||||
JANET_CORE_REG("os/sigaction", os_sigaction),
|
||||
#endif
|
||||
#endif
|
||||
JANET_REG_END
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -105,6 +105,15 @@ static int to_hex(uint8_t c) {
|
||||
}
|
||||
}
|
||||
|
||||
typedef int (*Consumer)(JanetParser *p, JanetParseState *state, uint8_t c);
|
||||
struct JanetParseState {
|
||||
int32_t counter;
|
||||
int32_t argn;
|
||||
int flags;
|
||||
size_t line;
|
||||
size_t column;
|
||||
Consumer consumer;
|
||||
};
|
||||
|
||||
/* Define a stack on the main parser struct */
|
||||
#define DEF_PARSER_STACK(NAME, T, STACK, STACKCOUNT, STACKCAP) \
|
||||
|
||||
173
src/core/peg.c
173
src/core/peg.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -194,41 +194,6 @@ tail:
|
||||
return memcmp(text, rule + 2, len) ? NULL : text + len;
|
||||
}
|
||||
|
||||
case RULE_DEBUG: {
|
||||
char buffer[32] = {0};
|
||||
size_t len = (size_t)(s->outer_text_end - text);
|
||||
memcpy(buffer, text, (len > 31 ? 31 : len));
|
||||
janet_eprintf("?? at [%s] (index %d)\n", buffer, (int32_t)(text - s->text_start));
|
||||
int has_color = janet_truthy(janet_dyn("err-color"));
|
||||
/* Accumulate buffer */
|
||||
if (s->scratch->count) {
|
||||
janet_eprintf("accumulate buffer: %v\n", janet_wrap_buffer(s->scratch));
|
||||
}
|
||||
/* Normal captures */
|
||||
if (s->captures->count) {
|
||||
janet_eprintf("stack [%d]:\n", s->captures->count);
|
||||
for (int32_t i = 0; i < s->captures->count; i++) {
|
||||
if (has_color) {
|
||||
janet_eprintf(" [%d]: %M\n", i, s->captures->data[i]);
|
||||
} else {
|
||||
janet_eprintf(" [%d]: %m\n", i, s->captures->data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Tagged captures */
|
||||
if (s->tagged_captures->count) {
|
||||
janet_eprintf("tag stack [%d]:\n", s->tagged_captures->count);
|
||||
for (int32_t i = 0; i < s->tagged_captures->count; i++) {
|
||||
if (has_color) {
|
||||
janet_eprintf(" [%d] tag=%d: %M\n", i, (int32_t) s->tags->data[i], s->tagged_captures->data[i]);
|
||||
} else {
|
||||
janet_eprintf(" [%d] tag=%d: %m\n", i, (int32_t) s->tags->data[i], s->tagged_captures->data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
case RULE_NCHAR: {
|
||||
uint32_t n = rule[1];
|
||||
return (text + n > s->text_end) ? NULL : text + n;
|
||||
@@ -377,7 +342,7 @@ tail:
|
||||
while (captured < hi) {
|
||||
CapState cs2 = cap_save(s);
|
||||
next_text = peg_rule(s, rule_a, text);
|
||||
if (!next_text || ((next_text == text) && (hi == UINT32_MAX))) {
|
||||
if (!next_text || next_text == text) {
|
||||
cap_load(s, cs2);
|
||||
break;
|
||||
}
|
||||
@@ -579,85 +544,45 @@ tail:
|
||||
return window_end;
|
||||
}
|
||||
|
||||
case RULE_TIL: {
|
||||
const uint32_t *rule_terminus = s->bytecode + rule[1];
|
||||
const uint32_t *rule_subpattern = s->bytecode + rule[2];
|
||||
|
||||
const uint8_t *terminus_start = text;
|
||||
const uint8_t *terminus_end = NULL;
|
||||
down1(s);
|
||||
while (terminus_start <= s->text_end) {
|
||||
CapState cs2 = cap_save(s);
|
||||
terminus_end = peg_rule(s, rule_terminus, terminus_start);
|
||||
cap_load(s, cs2);
|
||||
if (terminus_end) {
|
||||
break;
|
||||
}
|
||||
terminus_start++;
|
||||
}
|
||||
up1(s);
|
||||
|
||||
if (!terminus_end) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const uint8_t *saved_end = s->text_end;
|
||||
s->text_end = terminus_start;
|
||||
down1(s);
|
||||
const uint8_t *matched = peg_rule(s, rule_subpattern, text);
|
||||
up1(s);
|
||||
s->text_end = saved_end;
|
||||
|
||||
if (!matched) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return terminus_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 *chunk_start = text;
|
||||
const uint8_t *chunk_end = NULL;
|
||||
|
||||
while (text <= saved_end) {
|
||||
/* Find next split (or end of text) */
|
||||
const uint8_t *separator_end = NULL;
|
||||
do {
|
||||
const uint8_t *text_start = text;
|
||||
CapState cs = cap_save(s);
|
||||
down1(s);
|
||||
while (text <= saved_end) {
|
||||
chunk_end = text;
|
||||
const uint8_t *check = peg_rule(s, rule_separator, text);
|
||||
while (text <= s->text_end) {
|
||||
separator_end = peg_rule(s, rule_separator, text);
|
||||
cap_load(s, cs);
|
||||
if (check) {
|
||||
text = check;
|
||||
if (separator_end) {
|
||||
break;
|
||||
}
|
||||
text++;
|
||||
}
|
||||
up1(s);
|
||||
|
||||
/* Match between splits */
|
||||
s->text_end = chunk_end;
|
||||
if (separator_end) {
|
||||
s->text_end = text;
|
||||
text = separator_end;
|
||||
}
|
||||
|
||||
down1(s);
|
||||
const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, chunk_start);
|
||||
const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, text_start);
|
||||
up1(s);
|
||||
s->text_end = saved_end;
|
||||
if (!subpattern_end) return NULL; /* Don't match anything */
|
||||
|
||||
/* Ensure forward progress */
|
||||
if (text == chunk_start) return NULL;
|
||||
chunk_start = text;
|
||||
}
|
||||
if (!subpattern_end) {
|
||||
return NULL;
|
||||
}
|
||||
} while (separator_end);
|
||||
|
||||
s->text_end = saved_end;
|
||||
return s->text_end;
|
||||
}
|
||||
|
||||
case RULE_REPLACE:
|
||||
case RULE_MATCHSPLICE:
|
||||
case RULE_MATCHTIME: {
|
||||
uint32_t tag = rule[3];
|
||||
int oldmode = s->mode;
|
||||
@@ -698,17 +623,8 @@ tail:
|
||||
break;
|
||||
}
|
||||
cap_load_keept(s, cs);
|
||||
if (rule[0] != RULE_REPLACE && !janet_truthy(cap)) return NULL; /* matchtime or matchtime flatten */
|
||||
const Janet *elements = NULL;
|
||||
int32_t len = 0;
|
||||
if ((rule[0] == RULE_MATCHSPLICE) && janet_indexed_view(cap, &elements, &len)) {
|
||||
/* unpack and flatten capture */
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
pushcap(s, elements[i], tag);
|
||||
}
|
||||
} else {
|
||||
pushcap(s, cap, tag);
|
||||
}
|
||||
if (rule[0] == RULE_MATCHTIME && !janet_truthy(cap)) return NULL;
|
||||
pushcap(s, cap, tag);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1280,14 +1196,6 @@ static void spec_constant(Builder *b, int32_t argc, const Janet *argv) {
|
||||
emit_2(r, RULE_CONSTANT, emit_constant(b, argv[0]), tag);
|
||||
}
|
||||
|
||||
static void spec_debug(Builder *b, int32_t argc, const Janet *argv) {
|
||||
peg_arity(b, argc, 0, 0);
|
||||
Reserve r = reserve(b, 1);
|
||||
uint32_t empty = 0;
|
||||
(void) argv;
|
||||
emit_rule(r, RULE_DEBUG, 0, &empty);
|
||||
}
|
||||
|
||||
static void spec_replace(Builder *b, int32_t argc, const Janet *argv) {
|
||||
peg_arity(b, argc, 2, 3);
|
||||
Reserve r = reserve(b, 4);
|
||||
@@ -1297,7 +1205,7 @@ static void spec_replace(Builder *b, int32_t argc, const Janet *argv) {
|
||||
emit_3(r, RULE_REPLACE, subrule, constant, tag);
|
||||
}
|
||||
|
||||
static void spec_matchtime_impl(Builder *b, int32_t argc, const Janet *argv, uint32_t op) {
|
||||
static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
|
||||
peg_arity(b, argc, 2, 3);
|
||||
Reserve r = reserve(b, 4);
|
||||
uint32_t subrule = peg_compile1(b, argv[0]);
|
||||
@@ -1308,15 +1216,7 @@ static void spec_matchtime_impl(Builder *b, int32_t argc, const Janet *argv, uin
|
||||
}
|
||||
uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0;
|
||||
uint32_t cindex = emit_constant(b, fun);
|
||||
emit_3(r, op, subrule, cindex, tag);
|
||||
}
|
||||
|
||||
static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
|
||||
spec_matchtime_impl(b, argc, argv, RULE_MATCHTIME);
|
||||
}
|
||||
|
||||
static void spec_matchtime_splice(Builder *b, int32_t argc, const Janet *argv) {
|
||||
spec_matchtime_impl(b, argc, argv, RULE_MATCHSPLICE);
|
||||
emit_3(r, RULE_MATCHTIME, subrule, cindex, tag);
|
||||
}
|
||||
|
||||
static void spec_sub(Builder *b, int32_t argc, const Janet *argv) {
|
||||
@@ -1327,14 +1227,6 @@ static void spec_sub(Builder *b, int32_t argc, const Janet *argv) {
|
||||
emit_2(r, RULE_SUB, subrule1, subrule2);
|
||||
}
|
||||
|
||||
static void spec_til(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_TIL, subrule1, subrule2);
|
||||
}
|
||||
|
||||
static void spec_split(Builder *b, int32_t argc, const Janet *argv) {
|
||||
peg_fixarity(b, argc, 2);
|
||||
Reserve r = reserve(b, 3);
|
||||
@@ -1392,7 +1284,6 @@ static const SpecialPair peg_specials[] = {
|
||||
{"<-", spec_capture},
|
||||
{">", spec_look},
|
||||
{"?", spec_opt},
|
||||
{"??", spec_debug},
|
||||
{"accumulate", spec_accumulate},
|
||||
{"any", spec_any},
|
||||
{"argument", spec_argument},
|
||||
@@ -1403,11 +1294,9 @@ static const SpecialPair peg_specials[] = {
|
||||
{"between", spec_between},
|
||||
{"capture", spec_capture},
|
||||
{"choice", spec_choice},
|
||||
{"cms", spec_matchtime_splice},
|
||||
{"cmt", spec_matchtime},
|
||||
{"column", spec_column},
|
||||
{"constant", spec_constant},
|
||||
{"debug", spec_debug},
|
||||
{"drop", spec_drop},
|
||||
{"error", spec_error},
|
||||
{"group", spec_group},
|
||||
@@ -1434,7 +1323,6 @@ static const SpecialPair peg_specials[] = {
|
||||
{"split", spec_split},
|
||||
{"sub", spec_sub},
|
||||
{"thru", spec_thru},
|
||||
{"til", spec_til},
|
||||
{"to", spec_to},
|
||||
{"uint", spec_uint_le},
|
||||
{"uint-be", spec_uint_be},
|
||||
@@ -1528,11 +1416,6 @@ static uint32_t peg_compile1(Builder *b, Janet peg) {
|
||||
emit_bytes(b, RULE_LITERAL, len, str);
|
||||
break;
|
||||
}
|
||||
case JANET_BUFFER: {
|
||||
const JanetBuffer *buf = janet_unwrap_buffer(peg);
|
||||
emit_bytes(b, RULE_LITERAL, buf->count, buf->data);
|
||||
break;
|
||||
}
|
||||
case JANET_TABLE: {
|
||||
/* Build grammar table */
|
||||
JanetTable *new_grammar = janet_table_clone(janet_unwrap_table(peg));
|
||||
@@ -1661,7 +1544,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
|
||||
/* After here, no panics except for the bad: label. */
|
||||
|
||||
/* Keep track at each index if an instruction was
|
||||
* referenced (0x01) or is in a main bytecode position
|
||||
* reference (0x01) or is in a main bytecode position
|
||||
* (0x02). This lets us do a linear scan and not
|
||||
* need to a depth first traversal. It is stricter
|
||||
* than a dfs by not allowing certain kinds of unused
|
||||
@@ -1684,10 +1567,6 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
|
||||
case RULE_LITERAL:
|
||||
i += 2 + ((rule[1] + 3) >> 2);
|
||||
break;
|
||||
case RULE_DEBUG:
|
||||
/* [0 words] */
|
||||
i += 1;
|
||||
break;
|
||||
case RULE_NCHAR:
|
||||
case RULE_NOTNCHAR:
|
||||
case RULE_RANGE:
|
||||
@@ -1771,7 +1650,6 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
|
||||
break;
|
||||
case RULE_REPLACE:
|
||||
case RULE_MATCHTIME:
|
||||
case RULE_MATCHSPLICE:
|
||||
/* [rule, constant, tag] */
|
||||
if (rule[1] >= blen) goto bad;
|
||||
if (rule[2] >= clen) goto bad;
|
||||
@@ -1779,7 +1657,6 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
|
||||
i += 4;
|
||||
break;
|
||||
case RULE_SUB:
|
||||
case RULE_TIL:
|
||||
case RULE_SPLIT:
|
||||
/* [rule, rule] */
|
||||
if (rule[1] >= blen) goto bad;
|
||||
@@ -1903,8 +1780,8 @@ static JanetPeg *compile_peg(Janet x) {
|
||||
JANET_CORE_FN(cfun_peg_compile,
|
||||
"(peg/compile peg)",
|
||||
"Compiles a peg source data structure into a <core/peg>. This will speed up matching "
|
||||
"if the same peg will be used multiple times. `(dyn :peg-grammar)` replaces "
|
||||
"`default-peg-grammar` for the grammar of the peg.") {
|
||||
"if the same peg will be used multiple times. Will also use `(dyn :peg-grammar)` to supplement "
|
||||
"the grammar of the peg for otherwise undefined peg keywords.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetPeg *peg = compile_peg(argv[0]);
|
||||
return janet_wrap_abstract(peg);
|
||||
|
||||
227
src/core/pp.c
227
src/core/pp.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -72,7 +72,7 @@ static int count_dig10(int32_t x) {
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t integer_to_string_b(JanetBuffer *buffer, int32_t x) {
|
||||
static void integer_to_string_b(JanetBuffer *buffer, int32_t x) {
|
||||
janet_buffer_extra(buffer, BUFSIZE);
|
||||
uint8_t *buf = buffer->data + buffer->count;
|
||||
int32_t neg = 0;
|
||||
@@ -80,7 +80,7 @@ static int32_t integer_to_string_b(JanetBuffer *buffer, int32_t x) {
|
||||
if (x == 0) {
|
||||
buf[0] = '0';
|
||||
buffer->count++;
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
if (x > 0) {
|
||||
x = -x;
|
||||
@@ -96,7 +96,6 @@ static int32_t integer_to_string_b(JanetBuffer *buffer, int32_t x) {
|
||||
x /= 10;
|
||||
}
|
||||
buffer->count += len + neg;
|
||||
return len + neg;
|
||||
}
|
||||
|
||||
#define HEX(i) (((uint8_t *) janet_base64)[(i)])
|
||||
@@ -135,55 +134,43 @@ static void string_description_b(JanetBuffer *buffer, const char *title, void *p
|
||||
#undef POINTSIZE
|
||||
}
|
||||
|
||||
static int janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int32_t len) {
|
||||
static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int32_t len) {
|
||||
janet_buffer_push_u8(buffer, '"');
|
||||
int align = 1;
|
||||
for (int32_t i = 0; i < len; ++i) {
|
||||
uint8_t c = str[i];
|
||||
switch (c) {
|
||||
case '"':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\"", 2);
|
||||
align += 2;
|
||||
break;
|
||||
case '\n':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\n", 2);
|
||||
align += 2;
|
||||
break;
|
||||
case '\r':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\r", 2);
|
||||
align += 2;
|
||||
break;
|
||||
case '\0':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\0", 2);
|
||||
align += 2;
|
||||
break;
|
||||
case '\f':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\f", 2);
|
||||
align += 2;
|
||||
break;
|
||||
case '\v':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\v", 2);
|
||||
align += 2;
|
||||
break;
|
||||
case '\a':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\a", 2);
|
||||
align += 2;
|
||||
break;
|
||||
case '\b':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\b", 2);
|
||||
align += 2;
|
||||
break;
|
||||
case 27:
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\e", 2);
|
||||
align += 2;
|
||||
break;
|
||||
case '\\':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\\", 2);
|
||||
align += 2;
|
||||
break;
|
||||
case '\t':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\t", 2);
|
||||
align += 2;
|
||||
break;
|
||||
default:
|
||||
if (c < 32 || c > 126) {
|
||||
@@ -193,16 +180,13 @@ static int janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int
|
||||
buf[2] = janet_base64[(c >> 4) & 0xF];
|
||||
buf[3] = janet_base64[c & 0xF];
|
||||
janet_buffer_push_bytes(buffer, buf, 4);
|
||||
align += 4;
|
||||
} else {
|
||||
janet_buffer_push_u8(buffer, c);
|
||||
align++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
janet_buffer_push_u8(buffer, '"');
|
||||
return align + 1;
|
||||
}
|
||||
|
||||
static void janet_escape_string_b(JanetBuffer *buffer, const uint8_t *str) {
|
||||
@@ -374,7 +358,7 @@ const uint8_t *janet_to_string(Janet x) {
|
||||
struct pretty {
|
||||
JanetBuffer *buffer;
|
||||
int depth;
|
||||
int align;
|
||||
int indent;
|
||||
int flags;
|
||||
int32_t bufstartlen;
|
||||
int32_t *keysort_buffer;
|
||||
@@ -466,15 +450,14 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_newline(struct pretty *S, int align) {
|
||||
static void print_newline(struct pretty *S, int just_a_space) {
|
||||
int i;
|
||||
S->align = align;
|
||||
if (S->flags & JANET_PRETTY_ONELINE) {
|
||||
if (just_a_space || (S->flags & JANET_PRETTY_ONELINE)) {
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
return;
|
||||
}
|
||||
janet_buffer_push_u8(S->buffer, '\n');
|
||||
for (i = 0; i < S->align; i++) {
|
||||
for (i = 0; i < S->indent; i++) {
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
}
|
||||
}
|
||||
@@ -501,12 +484,13 @@ static const char *janet_pretty_colors[] = {
|
||||
"\x1B[36m"
|
||||
};
|
||||
|
||||
#define JANET_PRETTY_DICT_ONELINE 4
|
||||
#define JANET_PRETTY_IND_ONELINE 10
|
||||
#define JANET_PRETTY_DICT_LIMIT 30
|
||||
#define JANET_PRETTY_DICT_KEYSORT_LIMIT 2000
|
||||
#define JANET_PRETTY_ARRAY_LIMIT 160
|
||||
|
||||
/* Helper for pretty printing */
|
||||
static void janet_pretty_one(struct pretty *S, Janet x) {
|
||||
static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
/* Add to seen */
|
||||
switch (janet_type(x)) {
|
||||
case JANET_NIL:
|
||||
@@ -521,7 +505,7 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
|
||||
janet_buffer_push_cstring(S->buffer, janet_cycle_color);
|
||||
}
|
||||
janet_buffer_push_cstring(S->buffer, "<cycle ");
|
||||
S->align += 8 + integer_to_string_b(S->buffer, janet_unwrap_integer(seenid));
|
||||
integer_to_string_b(S->buffer, janet_unwrap_integer(seenid));
|
||||
janet_buffer_push_u8(S->buffer, '>');
|
||||
if (S->flags & JANET_PRETTY_COLOR) {
|
||||
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
|
||||
@@ -543,11 +527,9 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
|
||||
if (janet_checktype(x, JANET_BUFFER) && janet_unwrap_buffer(x) == S->buffer) {
|
||||
janet_buffer_ensure(S->buffer, S->buffer->count + S->bufstartlen * 4 + 3, 1);
|
||||
janet_buffer_push_u8(S->buffer, '@');
|
||||
S->align += 1 + janet_escape_string_impl(S->buffer, S->buffer->data, S->bufstartlen);
|
||||
janet_escape_string_impl(S->buffer, S->buffer->data, S->bufstartlen);
|
||||
} else {
|
||||
S->align -= S->buffer->count;
|
||||
janet_description_b(S->buffer, x);
|
||||
S->align += S->buffer->count;
|
||||
}
|
||||
if (color && (S->flags & JANET_PRETTY_COLOR)) {
|
||||
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
|
||||
@@ -564,34 +546,35 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
|
||||
const char *startstr = isarray ? "@[" : hasbrackets ? "[" : "(";
|
||||
const char endchar = isarray ? ']' : hasbrackets ? ']' : ')';
|
||||
janet_buffer_push_cstring(S->buffer, startstr);
|
||||
const int align = S->align += strlen(startstr);
|
||||
S->depth--;
|
||||
S->indent += 2;
|
||||
if (S->depth == 0) {
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
S->align += 3;
|
||||
} else {
|
||||
if (!isarray && !(S->flags & JANET_PRETTY_ONELINE) && len >= JANET_PRETTY_IND_ONELINE)
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
if (is_dict_value && len >= JANET_PRETTY_IND_ONELINE) print_newline(S, 0);
|
||||
if (len > JANET_PRETTY_ARRAY_LIMIT && !(S->flags & JANET_PRETTY_NOTRUNC)) {
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (i) print_newline(S, align);
|
||||
janet_pretty_one(S, arr[i]);
|
||||
if (i) print_newline(S, 0);
|
||||
janet_pretty_one(S, arr[i], 0);
|
||||
}
|
||||
print_newline(S, align);
|
||||
print_newline(S, 0);
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
S->align += 3;
|
||||
for (i = len - 3; i < len; i++) {
|
||||
print_newline(S, align);
|
||||
janet_pretty_one(S, arr[i]);
|
||||
for (i = 0; i < 3; i++) {
|
||||
print_newline(S, 0);
|
||||
janet_pretty_one(S, arr[len - 3 + i], 0);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i) print_newline(S, align);
|
||||
janet_pretty_one(S, arr[i]);
|
||||
if (i) print_newline(S, len < JANET_PRETTY_IND_ONELINE);
|
||||
janet_pretty_one(S, arr[i], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
S->indent -= 2;
|
||||
S->depth++;
|
||||
janet_buffer_push_u8(S->buffer, endchar);
|
||||
S->align++;
|
||||
break;
|
||||
}
|
||||
case JANET_STRUCT:
|
||||
@@ -602,7 +585,6 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
|
||||
if (istable) {
|
||||
JanetTable *t = janet_unwrap_table(x);
|
||||
JanetTable *proto = t->proto;
|
||||
S->align++;
|
||||
janet_buffer_push_cstring(S->buffer, "@");
|
||||
if (NULL != proto) {
|
||||
Janet name = janet_table_get(proto, janet_ckeywordv("_name"));
|
||||
@@ -613,7 +595,6 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
|
||||
janet_buffer_push_cstring(S->buffer, janet_class_color);
|
||||
}
|
||||
janet_buffer_push_bytes(S->buffer, n, len);
|
||||
S->align += len;
|
||||
if (S->flags & JANET_PRETTY_COLOR) {
|
||||
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
|
||||
}
|
||||
@@ -631,99 +612,73 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
|
||||
janet_buffer_push_cstring(S->buffer, janet_class_color);
|
||||
}
|
||||
janet_buffer_push_bytes(S->buffer, n, len);
|
||||
S->align += len;
|
||||
if (S->flags & JANET_PRETTY_COLOR) {
|
||||
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
janet_buffer_push_u8(S->buffer, '{');
|
||||
const int align = ++S->align;
|
||||
janet_buffer_push_cstring(S->buffer, "{");
|
||||
|
||||
S->depth--;
|
||||
S->indent += 2;
|
||||
if (S->depth == 0) {
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
S->align += 3;
|
||||
} else {
|
||||
int32_t len = 0, cap = 0;
|
||||
int32_t i = 0, len = 0, cap = 0;
|
||||
const JanetKV *kvs = NULL;
|
||||
janet_dictionary_view(x, &kvs, &len, &cap);
|
||||
if (!istable && !(S->flags & JANET_PRETTY_ONELINE) && len >= JANET_PRETTY_DICT_ONELINE)
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
if (is_dict_value && len >= JANET_PRETTY_DICT_ONELINE) print_newline(S, 0);
|
||||
int32_t ks_start = S->keysort_start;
|
||||
|
||||
/* Ensure buffer is large enough to sort keys. */
|
||||
int truncated = 0;
|
||||
|
||||
/* Shortcut for huge dictionaries, don't bother sorting keys */
|
||||
if (len > JANET_PRETTY_DICT_KEYSORT_LIMIT) {
|
||||
if (!(S->flags & JANET_PRETTY_NOTRUNC) && (len > JANET_PRETTY_DICT_LIMIT)) {
|
||||
len = JANET_PRETTY_DICT_LIMIT;
|
||||
truncated = 1;
|
||||
}
|
||||
int32_t j = 0;
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
while (janet_checktype(kvs[j].key, JANET_NIL)) j++;
|
||||
if (i) print_newline(S, align);
|
||||
janet_pretty_one(S, kvs[j].key);
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
S->align++;
|
||||
janet_pretty_one(S, kvs[j].value);
|
||||
j++;
|
||||
}
|
||||
if (truncated) {
|
||||
print_newline(S, align);
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
S->align += 3;
|
||||
}
|
||||
} else {
|
||||
/* Sorted keys dictionaries */
|
||||
|
||||
/* Ensure buffer is large enough to sort keys. */
|
||||
int64_t mincap = (int64_t) len + (int64_t) ks_start;
|
||||
if (mincap > INT32_MAX) {
|
||||
truncated = 1;
|
||||
len = 0;
|
||||
mincap = ks_start;
|
||||
}
|
||||
|
||||
if (S->keysort_capacity < mincap) {
|
||||
if (mincap >= INT32_MAX / 2) {
|
||||
S->keysort_capacity = INT32_MAX;
|
||||
} else {
|
||||
S->keysort_capacity = (int32_t)(mincap * 2);
|
||||
}
|
||||
S->keysort_buffer = janet_srealloc(S->keysort_buffer, sizeof(int32_t) * S->keysort_capacity);
|
||||
if (NULL == S->keysort_buffer) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
janet_sorted_keys(kvs, cap, S->keysort_buffer == NULL ? NULL : S->keysort_buffer + ks_start);
|
||||
S->keysort_start += len;
|
||||
if (!(S->flags & JANET_PRETTY_NOTRUNC) && (len > JANET_PRETTY_DICT_LIMIT)) {
|
||||
len = JANET_PRETTY_DICT_LIMIT;
|
||||
truncated = 1;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
if (i) print_newline(S, align);
|
||||
int32_t j = S->keysort_buffer[i + ks_start];
|
||||
janet_pretty_one(S, kvs[j].key);
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
S->align++;
|
||||
janet_pretty_one(S, kvs[j].value);
|
||||
}
|
||||
|
||||
if (truncated) {
|
||||
print_newline(S, align);
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
S->align += 3;
|
||||
}
|
||||
|
||||
int64_t mincap = (int64_t) len + (int64_t) ks_start;
|
||||
if (mincap > INT32_MAX) {
|
||||
truncated = 1;
|
||||
len = 0;
|
||||
mincap = ks_start;
|
||||
}
|
||||
|
||||
if (S->keysort_capacity < mincap) {
|
||||
if (mincap >= INT32_MAX / 2) {
|
||||
S->keysort_capacity = INT32_MAX;
|
||||
} else {
|
||||
S->keysort_capacity = (int32_t)(mincap * 2);
|
||||
}
|
||||
S->keysort_buffer = janet_srealloc(S->keysort_buffer, sizeof(int32_t) * S->keysort_capacity);
|
||||
if (NULL == S->keysort_buffer) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
janet_sorted_keys(kvs, cap, S->keysort_buffer == NULL ? NULL : S->keysort_buffer + ks_start);
|
||||
S->keysort_start += len;
|
||||
if (!(S->flags & JANET_PRETTY_NOTRUNC) && (len > JANET_PRETTY_DICT_LIMIT)) {
|
||||
len = JANET_PRETTY_DICT_LIMIT;
|
||||
truncated = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i) print_newline(S, len < JANET_PRETTY_DICT_ONELINE);
|
||||
int32_t j = S->keysort_buffer[i + ks_start];
|
||||
janet_pretty_one(S, kvs[j].key, 0);
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
janet_pretty_one(S, kvs[j].value, 1);
|
||||
}
|
||||
|
||||
if (truncated) {
|
||||
print_newline(S, 0);
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
}
|
||||
|
||||
S->keysort_start = ks_start;
|
||||
}
|
||||
S->indent -= 2;
|
||||
S->depth++;
|
||||
janet_buffer_push_u8(S->buffer, '}');
|
||||
S->align++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -739,14 +694,14 @@ static JanetBuffer *janet_pretty_(JanetBuffer *buffer, int depth, int flags, Jan
|
||||
}
|
||||
S.buffer = buffer;
|
||||
S.depth = depth;
|
||||
S.align = 0;
|
||||
S.indent = 0;
|
||||
S.flags = flags;
|
||||
S.bufstartlen = startlen;
|
||||
S.keysort_capacity = 0;
|
||||
S.keysort_buffer = NULL;
|
||||
S.keysort_start = 0;
|
||||
janet_table_init(&S.seen, 10);
|
||||
janet_pretty_one(&S, x);
|
||||
janet_pretty_one(&S, x, 0);
|
||||
janet_table_deinit(&S.seen);
|
||||
return S.buffer;
|
||||
}
|
||||
@@ -764,7 +719,7 @@ static JanetBuffer *janet_jdn_(JanetBuffer *buffer, int depth, Janet x, int32_t
|
||||
}
|
||||
S.buffer = buffer;
|
||||
S.depth = depth;
|
||||
S.align = 0;
|
||||
S.indent = 0;
|
||||
S.flags = 0;
|
||||
S.bufstartlen = startlen;
|
||||
S.keysort_capacity = 0;
|
||||
@@ -942,7 +897,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
|
||||
case 's':
|
||||
case 'S': {
|
||||
const char *str = va_arg(args, const char *);
|
||||
int32_t len = (c[-1] == 's')
|
||||
int32_t len = c[-1] == 's'
|
||||
? (int32_t) strlen(str)
|
||||
: janet_string_length((JanetString) str);
|
||||
if (form[2] == '\0')
|
||||
@@ -1068,20 +1023,10 @@ void janet_buffer_format(
|
||||
char form[MAX_FORMAT], item[MAX_ITEM];
|
||||
char width[3], precision[3];
|
||||
int nb = 0; /* number of bytes in added item */
|
||||
#ifdef JANET_PLAN9
|
||||
if (*strfrmt == 'r') {
|
||||
rerrstr(item, MAX_ITEM);
|
||||
nb = strlen(item);
|
||||
} else
|
||||
#endif
|
||||
if (++arg >= argc)
|
||||
janet_panic("not enough values for format");
|
||||
if (++arg >= argc)
|
||||
janet_panic("not enough values for format");
|
||||
strfrmt = scanformat(strfrmt, form, width, precision);
|
||||
switch (*strfrmt++) {
|
||||
#ifdef JANET_PLAN9
|
||||
case 'r':
|
||||
break;
|
||||
#endif
|
||||
case 'c': {
|
||||
nb = snprintf(item, MAX_ITEM, form, (int)
|
||||
janet_getinteger(argv, arg));
|
||||
@@ -1115,11 +1060,19 @@ void janet_buffer_format(
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
const char *s = janet_getcbytes(argv, arg);
|
||||
JanetByteView bytes = janet_getbytes(argv, arg);
|
||||
const uint8_t *s = bytes.bytes;
|
||||
int32_t l = bytes.len;
|
||||
if (form[2] == '\0')
|
||||
janet_buffer_push_cstring(b, s);
|
||||
janet_buffer_push_bytes(b, s, l);
|
||||
else {
|
||||
nb = snprintf(item, MAX_ITEM, form, s);
|
||||
if (l != (int32_t) strlen((const char *) s))
|
||||
janet_panic("string contains zeros");
|
||||
if (!strchr(form, '.') && l >= 100) {
|
||||
janet_panic("no precision and string is too long to be formatted");
|
||||
} else {
|
||||
nb = snprintf(item, MAX_ITEM, form, s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -26,10 +26,9 @@
|
||||
#include "state.h"
|
||||
#endif
|
||||
|
||||
/* Run a string of code. The return value is a set of error flags, JANET_DO_ERROR_RUNTIME, JANET_DO_ERROR_COMPILE, and JANET_DOR_ERROR_PARSE if
|
||||
* any errors were encountered in those phases. More information is printed to stderr. */
|
||||
/* Run a string */
|
||||
int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out) {
|
||||
JanetParser *parser;
|
||||
JanetParser parser;
|
||||
int errflags = 0, done = 0;
|
||||
int32_t index = 0;
|
||||
Janet ret = janet_wrap_nil();
|
||||
@@ -38,16 +37,14 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
|
||||
if (where) janet_gcroot(janet_wrap_string(where));
|
||||
if (NULL == sourcePath) sourcePath = "<unknown>";
|
||||
parser = janet_abstract(&janet_parser_type, sizeof(JanetParser));
|
||||
janet_parser_init(parser);
|
||||
janet_gcroot(janet_wrap_abstract(parser));
|
||||
janet_parser_init(&parser);
|
||||
|
||||
/* While we haven't seen an error */
|
||||
while (!done) {
|
||||
|
||||
/* Evaluate parsed values */
|
||||
while (janet_parser_has_more(parser)) {
|
||||
Janet form = janet_parser_produce(parser);
|
||||
while (janet_parser_has_more(&parser)) {
|
||||
Janet form = janet_parser_produce(&parser);
|
||||
JanetCompileResult cres = janet_compile(form, env, where);
|
||||
if (cres.status == JANET_COMPILE_OK) {
|
||||
JanetFunction *f = janet_thunk(cres.funcdef);
|
||||
@@ -56,30 +53,27 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
|
||||
if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
|
||||
janet_stacktrace_ext(fiber, ret, "");
|
||||
errflags |= JANET_DO_ERROR_RUNTIME;
|
||||
errflags |= 0x01;
|
||||
done = 1;
|
||||
}
|
||||
} else {
|
||||
int32_t line = (int32_t) parser->line;
|
||||
int32_t col = (int32_t) parser->column;
|
||||
ret = janet_wrap_string(cres.error);
|
||||
int32_t line = (int32_t) parser.line;
|
||||
int32_t col = (int32_t) parser.column;
|
||||
if ((cres.error_mapping.line > 0) &&
|
||||
(cres.error_mapping.column > 0)) {
|
||||
line = cres.error_mapping.line;
|
||||
col = cres.error_mapping.column;
|
||||
}
|
||||
JanetString ctx = janet_formatc("%s:%d:%d: compile error",
|
||||
sourcePath, line, col);
|
||||
JanetString errstr = janet_formatc("%s: %s",
|
||||
(const char *)ctx,
|
||||
(const char *)cres.error);
|
||||
ret = janet_wrap_string(errstr);
|
||||
if (cres.macrofiber) {
|
||||
janet_eprintf("%s", (const char *)ctx);
|
||||
janet_eprintf("%s:%d:%d: compile error", sourcePath,
|
||||
line, col);
|
||||
janet_stacktrace_ext(cres.macrofiber, ret, "");
|
||||
} else {
|
||||
janet_eprintf("%s\n", (const char *)errstr);
|
||||
janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath,
|
||||
line, col, (const char *)cres.error);
|
||||
}
|
||||
errflags |= JANET_DO_ERROR_COMPILE;
|
||||
errflags |= 0x02;
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
@@ -87,28 +81,26 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
if (done) break;
|
||||
|
||||
/* Dispatch based on parse state */
|
||||
switch (janet_parser_status(parser)) {
|
||||
switch (janet_parser_status(&parser)) {
|
||||
case JANET_PARSE_DEAD:
|
||||
done = 1;
|
||||
break;
|
||||
case JANET_PARSE_ERROR: {
|
||||
errflags |= JANET_DO_ERROR_PARSE;
|
||||
int32_t line = (int32_t) parser->line;
|
||||
int32_t col = (int32_t) parser->column;
|
||||
JanetString errstr = janet_formatc("%s:%d:%d: parse error: %s",
|
||||
sourcePath, line, col,
|
||||
janet_parser_error(parser));
|
||||
ret = janet_wrap_string(errstr);
|
||||
janet_eprintf("%s\n", (const char *)errstr);
|
||||
const char *e = janet_parser_error(&parser);
|
||||
errflags |= 0x04;
|
||||
ret = janet_cstringv(e);
|
||||
int32_t line = (int32_t) parser.line;
|
||||
int32_t col = (int32_t) parser.column;
|
||||
janet_eprintf("%s:%d:%d: parse error: %s\n", sourcePath, line, col, e);
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
case JANET_PARSE_ROOT:
|
||||
case JANET_PARSE_PENDING:
|
||||
if (index >= len) {
|
||||
janet_parser_eof(parser);
|
||||
janet_parser_eof(&parser);
|
||||
} else {
|
||||
janet_parser_consume(parser, bytes[index++]);
|
||||
janet_parser_consume(&parser, bytes[index++]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -116,7 +108,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
}
|
||||
|
||||
/* Clean up and return errors */
|
||||
janet_gcunroot(janet_wrap_abstract(parser));
|
||||
janet_parser_deinit(&parser);
|
||||
if (where) janet_gcunroot(janet_wrap_string(where));
|
||||
#ifdef JANET_EV
|
||||
/* Enter the event loop if we are not already in it */
|
||||
@@ -127,8 +119,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
janet_loop();
|
||||
if (fiber) {
|
||||
janet_gcunroot(janet_wrap_fiber(fiber));
|
||||
if (!errflags)
|
||||
ret = fiber->last_value;
|
||||
ret = fiber->last_value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -307,14 +307,14 @@ static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
/* Add attributes to a global def or var table */
|
||||
static JanetTable *handleattr(JanetCompiler *c, const char *kind, int32_t argn, const Janet *argv) {
|
||||
int32_t i;
|
||||
if (argn < 2) {
|
||||
janetc_error(c, janet_formatc("expected at least 2 arguments to %s", kind));
|
||||
return NULL;
|
||||
}
|
||||
JanetTable *tab = janet_table(2);
|
||||
const char *binding_name = janet_type(argv[0]) == JANET_SYMBOL
|
||||
? ((const char *)janet_unwrap_symbol(argv[0]))
|
||||
: "<multiple bindings>";
|
||||
if (argn < 2) {
|
||||
janetc_error(c, janet_formatc("expected at least 2 arguments to %s", kind));
|
||||
return NULL;
|
||||
}
|
||||
for (i = 1; i < argn - 1; i++) {
|
||||
Janet attr = argv[i];
|
||||
switch (janet_type(attr)) {
|
||||
@@ -362,23 +362,6 @@ SlotHeadPair *dohead_destructure(JanetCompiler *c, SlotHeadPair *into, JanetFopt
|
||||
janet_indexed_view(lhs, &view_lhs.items, &view_lhs.len);
|
||||
janet_indexed_view(rhs, &view_rhs.items, &view_rhs.len);
|
||||
int found_amp = 0;
|
||||
int found_splice = 0;
|
||||
/* Check for (def [x y z] [(splice [1 2 3]) 4 5 6]), bail out of optimization */
|
||||
for (int32_t i = 0; i < view_rhs.len; i++) {
|
||||
if (!janet_checktype(view_rhs.items[i], JANET_TUPLE)) {
|
||||
continue;
|
||||
}
|
||||
JanetTuple tup = janet_unwrap_tuple(view_rhs.items[i]);
|
||||
if (janet_tuple_length(tup) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (janet_symeq(tup[0], "splice")) {
|
||||
found_splice = 1;
|
||||
/* Good error will be generated later. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Check for (def [x & more] [1 2 3]), bail out of optimization */
|
||||
for (int32_t i = 0; i < view_lhs.len; i++) {
|
||||
if (janet_symeq(view_lhs.items[i], "&")) {
|
||||
found_amp = 1;
|
||||
@@ -386,7 +369,7 @@ SlotHeadPair *dohead_destructure(JanetCompiler *c, SlotHeadPair *into, JanetFopt
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_amp && !found_splice) {
|
||||
if (!found_amp) {
|
||||
for (int32_t i = 0; i < view_lhs.len; i++) {
|
||||
Janet sub_rhs = view_rhs.len <= i ? janet_wrap_nil() : view_rhs.items[i];
|
||||
into = dohead_destructure(c, into, subopts, view_lhs.items[i], sub_rhs);
|
||||
@@ -404,7 +387,7 @@ SlotHeadPair *dohead_destructure(JanetCompiler *c, SlotHeadPair *into, JanetFopt
|
||||
}
|
||||
|
||||
/* Def or var a symbol in a local scope */
|
||||
static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, JanetSlot ret, uint32_t def_flags) {
|
||||
static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, JanetSlot ret) {
|
||||
int isUnnamedRegister = !(ret.flags & JANET_SLOT_NAMED) &&
|
||||
ret.index > 0 &&
|
||||
ret.envindex >= 0;
|
||||
@@ -425,10 +408,7 @@ static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, Janet
|
||||
ret = localslot;
|
||||
}
|
||||
ret.flags |= flags;
|
||||
if (c->scope->flags & JANET_SCOPE_TOP) {
|
||||
def_flags |= JANET_DEFFLAG_NO_UNUSED;
|
||||
}
|
||||
janetc_nameslot(c, head, ret, def_flags);
|
||||
janetc_nameslot(c, head, ret);
|
||||
return !isUnnamedRegister;
|
||||
}
|
||||
|
||||
@@ -442,7 +422,8 @@ static int varleaf(
|
||||
JanetSlot refslot;
|
||||
JanetTable *entry = janet_table_clone(reftab);
|
||||
|
||||
int is_redef = c->is_redef;
|
||||
Janet redef_kw = janet_ckeywordv("redef");
|
||||
int is_redef = janet_truthy(janet_table_get(c->env, redef_kw));
|
||||
|
||||
JanetArray *ref;
|
||||
JanetBinding old_binding;
|
||||
@@ -462,21 +443,7 @@ static int varleaf(
|
||||
janetc_emit_ssu(c, JOP_PUT_INDEX, refslot, s, 0, 0);
|
||||
return 1;
|
||||
} else {
|
||||
int no_unused = reftab && reftab->count && janet_truthy(janet_table_get_keyword(reftab, "unused"));
|
||||
int no_shadow = reftab && reftab->count && janet_truthy(janet_table_get_keyword(reftab, "shadow"));
|
||||
uint32_t def_flags = 0;
|
||||
if (no_unused) def_flags |= JANET_DEFFLAG_NO_UNUSED;
|
||||
if (no_shadow) def_flags |= JANET_DEFFLAG_NO_SHADOWCHECK;
|
||||
return namelocal(c, sym, JANET_SLOT_MUTABLE, s, def_flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_metadata_lint(JanetCompiler *c, JanetTable *attr_table) {
|
||||
if (!(c->scope->flags & JANET_SCOPE_TOP) && attr_table && attr_table->count) {
|
||||
/* A macro is a normal lint, other metadata is a strict lint */
|
||||
if (janet_truthy(janet_table_get_keyword(attr_table, "macro"))) {
|
||||
janetc_lintf(c, JANET_C_LINT_NORMAL, "macro tag is ignored in inner scopes");
|
||||
}
|
||||
return namelocal(c, sym, JANET_SLOT_MUTABLE, s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,7 +453,6 @@ static JanetSlot janetc_var(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
if (c->result.status == JANET_COMPILE_ERROR) {
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
check_metadata_lint(c, attr_table);
|
||||
SlotHeadPair *into = NULL;
|
||||
into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]);
|
||||
if (c->result.status == JANET_COMPILE_ERROR) {
|
||||
@@ -508,15 +474,14 @@ static int defleaf(
|
||||
const uint8_t *sym,
|
||||
JanetSlot s,
|
||||
JanetTable *tab) {
|
||||
JanetTable *entry = NULL;
|
||||
int is_redef = 0;
|
||||
if (c->scope->flags & JANET_SCOPE_TOP) {
|
||||
entry = janet_table_clone(tab);
|
||||
JanetTable *entry = janet_table_clone(tab);
|
||||
janet_table_put(entry, janet_ckeywordv("source-map"),
|
||||
janet_wrap_tuple(janetc_make_sourcemap(c)));
|
||||
|
||||
is_redef = c->is_redef;
|
||||
if (is_redef) janet_table_put(entry, janet_ckeywordv("redef"), janet_wrap_true());
|
||||
Janet redef_kw = janet_ckeywordv("redef");
|
||||
int is_redef = janet_truthy(janet_table_get(c->env, redef_kw));
|
||||
if (is_redef) janet_table_put(entry, redef_kw, janet_wrap_true());
|
||||
|
||||
if (is_redef) {
|
||||
JanetBinding binding = janet_resolve_ext(c->env, sym);
|
||||
@@ -535,18 +500,11 @@ static int defleaf(
|
||||
JanetSlot tabslot = janetc_cslot(janet_wrap_table(entry));
|
||||
janetc_emit_sss(c, JOP_PUT, tabslot, valsym, s, 0);
|
||||
}
|
||||
}
|
||||
int no_unused = tab && tab->count && janet_truthy(janet_table_get_keyword(tab, "unused"));
|
||||
int no_shadow = is_redef || (tab && tab->count && janet_truthy(janet_table_get_keyword(tab, "shadow")));
|
||||
uint32_t def_flags = 0;
|
||||
if (no_unused) def_flags |= JANET_DEFFLAG_NO_UNUSED;
|
||||
if (no_shadow) def_flags |= JANET_DEFFLAG_NO_SHADOWCHECK;
|
||||
int result = namelocal(c, sym, 0, s, def_flags);
|
||||
if (entry) {
|
||||
/* Add env entry to env AFTER namelocal to avoid the shadowcheck false positive */
|
||||
|
||||
/* Add env entry to env */
|
||||
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(entry));
|
||||
}
|
||||
return result;
|
||||
return namelocal(c, sym, 0, s);
|
||||
}
|
||||
|
||||
static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
@@ -555,7 +513,6 @@ static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
if (c->result.status == JANET_COMPILE_ERROR) {
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
check_metadata_lint(c, attr_table);
|
||||
opts.flags &= ~JANET_FOPTS_HINT;
|
||||
SlotHeadPair *into = NULL;
|
||||
into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]);
|
||||
@@ -695,10 +652,8 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
|
||||
/* Write jumps - only add jump lengths if jump actually emitted */
|
||||
labeld = janet_v_count(c->buffer);
|
||||
if (labeljr < labeld) {
|
||||
c->buffer[labeljr] |= (labelr - labeljr) << 16;
|
||||
if (!tail && labeljd < labeld) c->buffer[labeljd] |= (labeld - labeljd) << 8;
|
||||
}
|
||||
c->buffer[labeljr] |= (labelr - labeljr) << 16;
|
||||
if (!tail) c->buffer[labeljd] |= (labeld - labeljd) << 8;
|
||||
|
||||
if (tail) target.flags |= JANET_SLOT_RETURNED;
|
||||
return target;
|
||||
@@ -920,7 +875,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
janetc_regalloc_freetemp(&c->scope->ra, tempself, JANETC_REGTEMP_0);
|
||||
/* Compile function */
|
||||
JanetFuncDef *def = janetc_pop_funcdef(c);
|
||||
def->name = janet_cstring("while");
|
||||
def->name = janet_cstring("_while");
|
||||
janet_def_addflags(def);
|
||||
int32_t defindex = janetc_addfuncdef(c, def);
|
||||
/* And then load the closure and call it. */
|
||||
@@ -1077,24 +1032,16 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
named_table = janet_table(10);
|
||||
named_slot = janetc_farslot(c);
|
||||
} else {
|
||||
janetc_nameslot(c, sym, janetc_farslot(c), 0);
|
||||
janetc_nameslot(c, sym, janetc_farslot(c));
|
||||
}
|
||||
} else {
|
||||
janetc_nameslot(c, sym, janetc_farslot(c), 0);
|
||||
janetc_nameslot(c, sym, janetc_farslot(c));
|
||||
}
|
||||
} else {
|
||||
janet_v_push(destructed_params, janetc_farslot(c));
|
||||
}
|
||||
}
|
||||
|
||||
/* Compile named arguments */
|
||||
if (namedargs) {
|
||||
Janet param = janet_wrap_table(named_table);
|
||||
destructure(c, param, named_slot, defleaf, NULL);
|
||||
janetc_freeslot(c, named_slot);
|
||||
janet_v_free(named_params);
|
||||
}
|
||||
|
||||
/* Compile destructed params */
|
||||
int32_t j = 0;
|
||||
for (i = 0; i < paramcount; i++) {
|
||||
@@ -1108,6 +1055,14 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
}
|
||||
janet_v_free(destructed_params);
|
||||
|
||||
/* Compile named arguments */
|
||||
if (namedargs) {
|
||||
Janet param = janet_wrap_table(named_table);
|
||||
destructure(c, param, named_slot, defleaf, NULL);
|
||||
janetc_freeslot(c, named_slot);
|
||||
janet_v_free(named_params);
|
||||
}
|
||||
|
||||
max_arity = (vararg || allow_extra) ? INT32_MAX : arity;
|
||||
if (!seenopt) min_arity = arity;
|
||||
|
||||
@@ -1129,9 +1084,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
JanetSlot slot = janetc_farslot(c);
|
||||
slot.flags = JANET_SLOT_NAMED | JANET_FUNCTION;
|
||||
janetc_emit_s(c, JOP_LOAD_SELF, slot, 1);
|
||||
/* We should figure out a better way to avoid `(def x 1) (def x :shadow (fn x [...] ...))` triggering a
|
||||
* shadow lint for the last x */
|
||||
janetc_nameslot(c, sym, slot, JANET_DEFFLAG_NO_UNUSED | JANET_DEFFLAG_NO_SHADOWCHECK);
|
||||
janetc_nameslot(c, sym, slot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1152,12 +1105,8 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
def->arity = arity;
|
||||
def->min_arity = min_arity;
|
||||
def->max_arity = max_arity;
|
||||
if (named_table != NULL) {
|
||||
def->named_args_count = named_table->count;
|
||||
}
|
||||
if (vararg) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
|
||||
if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
|
||||
if (namedargs) def->flags |= JANET_FUNCDEF_FLAG_NAMEDARGS;
|
||||
|
||||
if (hasname) def->name = janet_unwrap_symbol(head); /* Also correctly unwraps keyword */
|
||||
janet_def_addflags(def);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -23,16 +23,11 @@
|
||||
#ifndef JANET_STATE_H_defined
|
||||
#define JANET_STATE_H_defined
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include "features.h"
|
||||
#include <janet.h>
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef JANET_EV
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <windows.h>
|
||||
#else
|
||||
#ifndef JANET_WINDOWS
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#endif
|
||||
@@ -58,22 +53,13 @@ typedef struct {
|
||||
void *data;
|
||||
} JanetQueue;
|
||||
|
||||
#ifdef JANET_EV
|
||||
typedef struct {
|
||||
JanetTimestamp when;
|
||||
JanetFiber *fiber;
|
||||
JanetFiber *curr_fiber;
|
||||
uint32_t sched_id;
|
||||
int is_error;
|
||||
int has_worker;
|
||||
#ifdef JANET_WINDOWS
|
||||
HANDLE worker;
|
||||
HANDLE worker_event;
|
||||
#else
|
||||
pthread_t worker;
|
||||
#endif
|
||||
} JanetTimeout;
|
||||
#endif
|
||||
|
||||
/* Registry table for C functions - contains metadata that can
|
||||
* be looked up by cfunction pointer. All strings here are pointing to
|
||||
@@ -182,8 +168,6 @@ struct JanetVM {
|
||||
JanetTable signal_handlers;
|
||||
#ifdef JANET_WINDOWS
|
||||
void **iocp;
|
||||
void *connect_ex; /* MSWsock extension if available */
|
||||
int connect_ex_loaded;
|
||||
#elif defined(JANET_EV_EPOLL)
|
||||
pthread_attr_t new_thread_attr;
|
||||
JanetHandle selfpipe[2];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -49,8 +49,6 @@
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#define JANET_NUMBER_LENGTH_RIDICULOUS 0xFFFF
|
||||
|
||||
/* Lookup table for getting values of characters when parsing numbers. Handles
|
||||
* digits 0-9 and a-z (and A-Z). A-Z have values of 10 to 35. */
|
||||
static uint8_t digit_lookup[128] = {
|
||||
@@ -268,7 +266,7 @@ int janet_scan_number_base(
|
||||
* the decimal point, exponent could wrap around and become positive. It's
|
||||
* easier to reject ridiculously large inputs than to check for overflows.
|
||||
* */
|
||||
if (len > JANET_NUMBER_LENGTH_RIDICULOUS) goto error;
|
||||
if (len > INT32_MAX / 40) goto error;
|
||||
|
||||
/* Get sign */
|
||||
if (str >= end) goto error;
|
||||
@@ -303,7 +301,6 @@ int janet_scan_number_base(
|
||||
if (base == 0) {
|
||||
base = 10;
|
||||
}
|
||||
int exp_base = base;
|
||||
|
||||
/* Skip leading zeros */
|
||||
while (str < end && (*str == '0' || *str == '.')) {
|
||||
@@ -325,12 +322,6 @@ int janet_scan_number_base(
|
||||
} else if (*str == '&') {
|
||||
foundexp = 1;
|
||||
break;
|
||||
} else if (base == 16 && (*str == 'P' || *str == 'p')) { /* IEEE hex float */
|
||||
foundexp = 1;
|
||||
exp_base = 10;
|
||||
base = 2;
|
||||
ex *= 4; /* We need to correct the current exponent after we change the base */
|
||||
break;
|
||||
} else if (base == 10 && (*str == 'E' || *str == 'e')) {
|
||||
foundexp = 1;
|
||||
break;
|
||||
@@ -369,9 +360,9 @@ int janet_scan_number_base(
|
||||
}
|
||||
while (str < end) {
|
||||
int digit = digit_lookup[*str & 0x7F];
|
||||
if (*str > 127 || digit >= exp_base) goto error;
|
||||
if (*str > 127 || digit >= base) goto error;
|
||||
if (ee < (INT32_MAX / 40)) {
|
||||
ee = exp_base * ee + digit;
|
||||
ee = base * ee + digit;
|
||||
}
|
||||
str++;
|
||||
seenadigit = 1;
|
||||
@@ -412,7 +403,10 @@ static int scan_uint64(
|
||||
*neg = 0;
|
||||
*out = 0;
|
||||
uint64_t accum = 0;
|
||||
if (len > JANET_NUMBER_LENGTH_RIDICULOUS) return 0;
|
||||
/* len max is INT64_MAX in base 2 with _ between each bits */
|
||||
/* '2r' + 64 bits + 63 _ + sign = 130 => 150 for some leading */
|
||||
/* zeros */
|
||||
if (len > 150) return 0;
|
||||
/* Get sign */
|
||||
if (str >= end) return 0;
|
||||
if (*str == '-') {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -155,17 +155,6 @@ Janet janet_table_get(JanetTable *t, Janet key) {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Used internally for compiler stuff */
|
||||
Janet janet_table_get_keyword(JanetTable *t, const char *keyword) {
|
||||
int32_t keyword_len = (int32_t) strlen(keyword);
|
||||
for (int i = JANET_MAX_PROTO_DEPTH; t && i; t = t->proto, --i) {
|
||||
JanetKV *bucket = (JanetKV *) janet_dict_find_keyword(t->data, t->capacity, (const uint8_t *) keyword, keyword_len);
|
||||
if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL))
|
||||
return bucket->value;
|
||||
}
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Get a value out of the table, and record which prototype it was from. */
|
||||
Janet janet_table_get_ex(JanetTable *t, Janet key, JanetTable **which) {
|
||||
for (int i = JANET_MAX_PROTO_DEPTH; t && i; t = t->proto, --i) {
|
||||
@@ -383,14 +372,12 @@ JANET_CORE_FN(cfun_table_setproto,
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_table_tostruct,
|
||||
"(table/to-struct tab &opt proto)",
|
||||
"Convert a table to a struct. Returns a new struct.") {
|
||||
janet_arity(argc, 1, 2);
|
||||
"(table/to-struct tab)",
|
||||
"Convert a table to a struct. Returns a new struct. This function "
|
||||
"does not take into account prototype tables.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTable *t = janet_gettable(argv, 0);
|
||||
JanetStruct proto = janet_optstruct(argv, argc, 1, NULL);
|
||||
JanetStruct st = janet_table_to_struct(t);
|
||||
janet_struct_proto(st) = proto;
|
||||
return janet_wrap_struct(st);
|
||||
return janet_wrap_struct(janet_table_to_struct(t));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_table_rawget,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
116
src/core/util.c
116
src/core/util.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -268,7 +268,7 @@ int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len) {
|
||||
return (int32_t) hash;
|
||||
}
|
||||
|
||||
/* Calculate next power of 2. May overflow. If n < 0,
|
||||
/* Calculate next power of 2. May overflow. If n is 0,
|
||||
* will return 0. */
|
||||
int32_t janet_tablen(int32_t n) {
|
||||
if (n < 0) return 0;
|
||||
@@ -321,54 +321,6 @@ const JanetKV *janet_dict_find(const JanetKV *buckets, int32_t cap, Janet key) {
|
||||
return first_bucket;
|
||||
}
|
||||
|
||||
/* Helper to find a keyword, symbol, or string in a Janet struct or table without allocating
|
||||
* memory or needing to find interned symbols */
|
||||
const JanetKV *janet_dict_find_keyword(
|
||||
const JanetKV *buckets, int32_t cap,
|
||||
const uint8_t *cstr, int32_t cstr_len) {
|
||||
int32_t hash = janet_string_calchash(cstr, cstr_len);
|
||||
int32_t index = janet_maphash(cap, hash);
|
||||
int32_t i;
|
||||
const JanetKV *first_bucket = NULL;
|
||||
/* Higher half */
|
||||
for (i = index; i < cap; i++) {
|
||||
const JanetKV *kv = buckets + i;
|
||||
if (janet_checktype(kv->key, JANET_NIL)) {
|
||||
if (janet_checktype(kv->value, JANET_NIL)) {
|
||||
return kv;
|
||||
} else if (NULL == first_bucket) {
|
||||
first_bucket = kv;
|
||||
}
|
||||
} else if (janet_checktype(kv->key, JANET_KEYWORD)) {
|
||||
/* Works for symbol and keyword, too */
|
||||
JanetString str = janet_unwrap_string(kv->key);
|
||||
int32_t len = janet_string_length(str);
|
||||
if (hash == janet_string_hash(str) && len == cstr_len && !memcmp(str, cstr, len)) {
|
||||
return buckets + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Lower half */
|
||||
for (i = 0; i < index; i++) {
|
||||
const JanetKV *kv = buckets + i;
|
||||
if (janet_checktype(kv->key, JANET_NIL)) {
|
||||
if (janet_checktype(kv->value, JANET_NIL)) {
|
||||
return kv;
|
||||
} else if (NULL == first_bucket) {
|
||||
first_bucket = kv;
|
||||
}
|
||||
} else if (janet_checktype(kv->key, JANET_KEYWORD)) {
|
||||
/* Works for symbol and keyword, too */
|
||||
JanetString str = janet_unwrap_string(kv->key);
|
||||
int32_t len = janet_string_length(str);
|
||||
if (hash == janet_string_hash(str) && len == cstr_len && !memcmp(str, cstr, len)) {
|
||||
return buckets + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return first_bucket;
|
||||
}
|
||||
|
||||
/* Get a value from a janet struct or table. */
|
||||
Janet janet_dictionary_get(const JanetKV *data, int32_t cap, Janet key) {
|
||||
const JanetKV *kv = janet_dict_find(data, cap, key);
|
||||
@@ -573,24 +525,8 @@ static char *namebuf_name(NameBuf *namebuf, const char *suffix) {
|
||||
return (char *)(namebuf->buf);
|
||||
}
|
||||
|
||||
/* Add a little bit of safety when using nanboxing on arm. Instead of inserting run-time checks everywhere, we are
|
||||
* only doing it during registration which has much less cost (1 shift and mask). */
|
||||
static void janet_check_pointer_align(void *p) {
|
||||
(void) p;
|
||||
#if defined(JANET_NANBOX_64) && JANET_NANBOX_64_POINTER_SHIFT != 0
|
||||
union {
|
||||
void *p;
|
||||
uintptr_t u;
|
||||
} un;
|
||||
un.p = p;
|
||||
janet_assert(!(un.u & (uintptr_t) ((1 << JANET_NANBOX_64_POINTER_SHIFT) - 1)),
|
||||
"unaligned pointer wrap - cfunction pointers and abstract types must be aligned with this nanboxing configuration.");
|
||||
#endif
|
||||
}
|
||||
|
||||
void janet_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns) {
|
||||
while (cfuns->name) {
|
||||
janet_check_pointer_align(cfuns->cfun);
|
||||
Janet fun = janet_wrap_cfunction(cfuns->cfun);
|
||||
if (env) janet_def(env, cfuns->name, fun, cfuns->documentation);
|
||||
janet_registry_put(cfuns->cfun, cfuns->name, regprefix, NULL, 0);
|
||||
@@ -600,7 +536,6 @@ void janet_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns)
|
||||
|
||||
void janet_cfuns_ext(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns) {
|
||||
while (cfuns->name) {
|
||||
janet_check_pointer_align(cfuns->cfun);
|
||||
Janet fun = janet_wrap_cfunction(cfuns->cfun);
|
||||
if (env) janet_def_sm(env, cfuns->name, fun, cfuns->documentation, cfuns->source_file, cfuns->source_line);
|
||||
janet_registry_put(cfuns->cfun, cfuns->name, regprefix, cfuns->source_file, cfuns->source_line);
|
||||
@@ -612,7 +547,6 @@ void janet_cfuns_prefix(JanetTable *env, const char *regprefix, const JanetReg *
|
||||
NameBuf nb;
|
||||
if (env) namebuf_init(&nb, regprefix);
|
||||
while (cfuns->name) {
|
||||
janet_check_pointer_align(cfuns->cfun);
|
||||
Janet fun = janet_wrap_cfunction(cfuns->cfun);
|
||||
if (env) janet_def(env, namebuf_name(&nb, cfuns->name), fun, cfuns->documentation);
|
||||
janet_registry_put(cfuns->cfun, cfuns->name, regprefix, NULL, 0);
|
||||
@@ -625,7 +559,6 @@ void janet_cfuns_ext_prefix(JanetTable *env, const char *regprefix, const JanetR
|
||||
NameBuf nb;
|
||||
if (env) namebuf_init(&nb, regprefix);
|
||||
while (cfuns->name) {
|
||||
janet_check_pointer_align(cfuns->cfun);
|
||||
Janet fun = janet_wrap_cfunction(cfuns->cfun);
|
||||
if (env) janet_def_sm(env, namebuf_name(&nb, cfuns->name), fun, cfuns->documentation, cfuns->source_file, cfuns->source_line);
|
||||
janet_registry_put(cfuns->cfun, cfuns->name, regprefix, cfuns->source_file, cfuns->source_line);
|
||||
@@ -642,7 +575,6 @@ void janet_register(const char *name, JanetCFunction cfun) {
|
||||
/* Abstract type introspection */
|
||||
|
||||
void janet_register_abstract_type(const JanetAbstractType *at) {
|
||||
janet_check_pointer_align((void *) at);
|
||||
Janet sym = janet_csymbolv(at->name);
|
||||
Janet check = janet_table_get(janet_vm.abstract_registry, sym);
|
||||
if (!janet_checktype(check, JANET_NIL) && at != janet_unwrap_pointer(check)) {
|
||||
@@ -675,7 +607,6 @@ void janet_core_def_sm(JanetTable *env, const char *name, Janet x, const void *p
|
||||
void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns) {
|
||||
(void) regprefix;
|
||||
while (cfuns->name) {
|
||||
janet_check_pointer_align(cfuns->cfun);
|
||||
Janet fun = janet_wrap_cfunction(cfuns->cfun);
|
||||
janet_table_put(env, janet_csymbolv(cfuns->name), fun);
|
||||
janet_registry_put(cfuns->cfun, cfuns->name, regprefix, cfuns->source_file, cfuns->source_line);
|
||||
@@ -697,11 +628,8 @@ JanetBinding janet_binding_from_entry(Janet entry) {
|
||||
return binding;
|
||||
entry_table = janet_unwrap_table(entry);
|
||||
|
||||
Janet deprecate = janet_table_get_keyword(entry_table, "deprecated");
|
||||
int macro = janet_truthy(janet_table_get_keyword(entry_table, "macro"));
|
||||
Janet value = janet_table_get_keyword(entry_table, "value");
|
||||
Janet ref = janet_table_get_keyword(entry_table, "ref");
|
||||
|
||||
/* deprecation check */
|
||||
Janet deprecate = janet_table_get(entry_table, janet_ckeywordv("deprecated"));
|
||||
if (janet_checktype(deprecate, JANET_KEYWORD)) {
|
||||
JanetKeyword depkw = janet_unwrap_keyword(deprecate);
|
||||
if (!janet_cstrcmp(depkw, "relaxed")) {
|
||||
@@ -715,8 +643,11 @@ JanetBinding janet_binding_from_entry(Janet entry) {
|
||||
binding.deprecation = JANET_BINDING_DEP_NORMAL;
|
||||
}
|
||||
|
||||
int macro = janet_truthy(janet_table_get(entry_table, janet_ckeywordv("macro")));
|
||||
Janet value = janet_table_get(entry_table, janet_ckeywordv("value"));
|
||||
Janet ref = janet_table_get(entry_table, janet_ckeywordv("ref"));
|
||||
int ref_is_valid = janet_checktype(ref, JANET_ARRAY);
|
||||
int redef = ref_is_valid && janet_truthy(janet_table_get_keyword(entry_table, "redef"));
|
||||
int redef = ref_is_valid && janet_truthy(janet_table_get(entry_table, janet_ckeywordv("redef")));
|
||||
|
||||
if (macro) {
|
||||
binding.value = redef ? ref : value;
|
||||
@@ -917,15 +848,11 @@ int janet_checksize(Janet x) {
|
||||
return 0;
|
||||
double dval = janet_unwrap_number(x);
|
||||
if (dval != (double)((size_t) dval)) return 0;
|
||||
#ifdef JANET_PLAN9
|
||||
return dval <= SIZE_MAX;
|
||||
#else
|
||||
if (SIZE_MAX > JANET_INTMAX_INT64) {
|
||||
return dval <= JANET_INTMAX_INT64;
|
||||
} else {
|
||||
return dval <= SIZE_MAX;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JanetTable *janet_get_core_table(const char *name) {
|
||||
@@ -1004,24 +931,27 @@ int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
|
||||
if (source == JANET_TIME_CPUTIME) {
|
||||
clock_t tmp = clock();
|
||||
spec->tv_sec = tmp / CLOCKS_PER_SEC;
|
||||
spec->tv_nsec = ((tmp - (spec->tv_sec * CLOCKS_PER_SEC)) * 1000000000) / CLOCKS_PER_SEC;
|
||||
} else {
|
||||
if (source == JANET_TIME_REALTIME) {
|
||||
clock_serv_t cclock;
|
||||
mach_timespec_t mts;
|
||||
clock_id_t cid = CALENDAR_CLOCK;
|
||||
if (source == JANET_TIME_REALTIME) {
|
||||
cid = CALENDAR_CLOCK;
|
||||
} else if (source == JANET_TIME_MONOTONIC) {
|
||||
cid = SYSTEM_CLOCK;
|
||||
}
|
||||
host_get_clock_service(mach_host_self(), cid, &cclock);
|
||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
||||
clock_get_time(cclock, &mts);
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
spec->tv_sec = mts.tv_sec;
|
||||
spec->tv_nsec = mts.tv_nsec;
|
||||
} else if (source == JANET_TIME_MONOTONIC) {
|
||||
clock_serv_t cclock;
|
||||
int nsecs;
|
||||
mach_msg_type_number_t count;
|
||||
host_get_clock_service(mach_host_self(), clock, &cclock);
|
||||
clock_get_attributes(cclock, CLOCK_GET_TIME_RES, (clock_attr_t)&nsecs, &count);
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
clock_getres(CLOCK_MONOTONIC, spec);
|
||||
}
|
||||
if (source == JANET_TIME_CPUTIME) {
|
||||
clock_t tmp = clock();
|
||||
spec->tv_sec = tmp;
|
||||
spec->tv_nsec = (tmp - spec->tv_sec) * 1.0e9;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -66,72 +66,42 @@
|
||||
|
||||
/* Utils */
|
||||
uint32_t janet_hash_mix(uint32_t input, uint32_t more);
|
||||
|
||||
#define janet_maphash(cap, hash) ((uint32_t)(hash) & (cap - 1))
|
||||
|
||||
int janet_valid_utf8(const uint8_t *str, int32_t len);
|
||||
|
||||
int janet_is_symbol_char(uint8_t c);
|
||||
|
||||
extern const char janet_base64[65];
|
||||
|
||||
int32_t janet_array_calchash(const Janet *array, int32_t len);
|
||||
|
||||
int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len);
|
||||
|
||||
int32_t janet_string_calchash(const uint8_t *str, int32_t len);
|
||||
|
||||
int32_t janet_tablen(int32_t n);
|
||||
|
||||
void safe_memcpy(void *dest, const void *src, size_t len);
|
||||
|
||||
void janet_buffer_push_types(JanetBuffer *buffer, int types);
|
||||
|
||||
const JanetKV *janet_dict_find(const JanetKV *buckets, int32_t cap, Janet key);
|
||||
|
||||
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,
|
||||
size_t itemsize,
|
||||
const uint8_t *key);
|
||||
|
||||
void janet_buffer_format(
|
||||
JanetBuffer *b,
|
||||
const char *strfrmt,
|
||||
int32_t argstart,
|
||||
int32_t argc,
|
||||
Janet *argv);
|
||||
|
||||
Janet janet_next_impl(Janet ds, Janet key, int is_interpreter);
|
||||
|
||||
JanetBinding janet_binding_from_entry(Janet entry);
|
||||
|
||||
JanetByteView janet_text_substitution(
|
||||
Janet *subst,
|
||||
const uint8_t *bytes,
|
||||
uint32_t len,
|
||||
JanetArray *extra_args);
|
||||
|
||||
const JanetKV *janet_dict_find_keyword(
|
||||
const JanetKV *buckets,
|
||||
int32_t cap,
|
||||
const uint8_t *cstr,
|
||||
int32_t cstr_len);
|
||||
|
||||
Janet janet_table_get_keyword(JanetTable *t, const char *keyword);
|
||||
|
||||
/* Registry functions */
|
||||
void janet_registry_put(
|
||||
JanetCFunction key,
|
||||
@@ -197,26 +167,7 @@ typedef void *Clib;
|
||||
#endif
|
||||
char *get_processed_name(const char *name);
|
||||
|
||||
#ifdef JANET_PLAN9
|
||||
#define RETRY_EINTR(RC, CALL) (RC) = CALL;
|
||||
#else
|
||||
#define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR)
|
||||
#endif
|
||||
|
||||
#ifdef JANET_EV
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
typedef struct {
|
||||
union {
|
||||
OVERLAPPED overlapped;
|
||||
WSAOVERLAPPED wsaoverlapped;
|
||||
} as;
|
||||
uint32_t bytes_transfered;
|
||||
} JanetOverlapped;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Initialize builtin libraries */
|
||||
void janet_lib_io(JanetTable *env);
|
||||
@@ -254,9 +205,9 @@ int janet_make_pipe(JanetHandle handles[2], int mode);
|
||||
#ifdef JANET_FILEWATCH
|
||||
void janet_lib_filewatch(JanetTable *env);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef JANET_FFI
|
||||
void janet_lib_ffi(JanetTable *env);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -322,8 +322,7 @@ int32_t janet_hash(Janet x) {
|
||||
break;
|
||||
case JANET_TUPLE:
|
||||
hash = janet_tuple_hash(janet_unwrap_tuple(x));
|
||||
uint32_t inc = (janet_tuple_flag(janet_unwrap_tuple(x)) & JANET_TUPLE_FLAG_BRACKETCTOR) ? 1 : 0;
|
||||
hash = (int32_t)((uint32_t)hash + inc); /* avoid overflow undefined behavior */
|
||||
hash += (janet_tuple_flag(janet_unwrap_tuple(x)) & JANET_TUPLE_FLAG_BRACKETCTOR) ? 1 : 0;
|
||||
break;
|
||||
case JANET_STRUCT:
|
||||
hash = janet_struct_hash(janet_unwrap_struct(x));
|
||||
@@ -335,9 +334,10 @@ int32_t janet_hash(Janet x) {
|
||||
} as;
|
||||
as.d = janet_unwrap_number(x);
|
||||
as.d += 0.0; /* normalize negative 0 */
|
||||
as.u = murmur64(as.u);
|
||||
uint32_t lo = (uint32_t)(as.u & 0xFFFFFFFF);
|
||||
uint32_t hi = (uint32_t)(as.u >> 32);
|
||||
hash = (int32_t)hi;
|
||||
uint32_t hilo = (hi ^ lo) * 2654435769u;
|
||||
hash = (int32_t)((hilo << 16) | (hilo >> 16));
|
||||
break;
|
||||
}
|
||||
case JANET_ABSTRACT: {
|
||||
@@ -495,7 +495,7 @@ Janet janet_in(Janet ds, Janet key) {
|
||||
if (!(type->get)(janet_unwrap_abstract(ds), key, &value))
|
||||
janet_panicf("key %v not found in %v ", key, ds);
|
||||
} else {
|
||||
janet_panicf("no getter for %v", ds);
|
||||
janet_panicf("no getter for %v ", ds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -622,7 +622,7 @@ Janet janet_getindex(Janet ds, int32_t index) {
|
||||
if (!(type->get)(janet_unwrap_abstract(ds), janet_wrap_integer(index), &value))
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
janet_panicf("no getter for %v", ds);
|
||||
janet_panicf("no getter for %v ", ds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -724,9 +724,6 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
|
||||
JanetArray *array = janet_unwrap_array(ds);
|
||||
if (index >= array->count) {
|
||||
janet_array_ensure(array, index + 1, 2);
|
||||
for (int32_t i = array->count; i < index + 1; i++) {
|
||||
array->data[i] = janet_wrap_nil();
|
||||
}
|
||||
array->count = index + 1;
|
||||
}
|
||||
array->data[index] = value;
|
||||
@@ -738,7 +735,6 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
|
||||
janet_panicf("can only put integers in buffers, got %v", value);
|
||||
if (index >= buffer->count) {
|
||||
janet_buffer_ensure(buffer, index + 1, 2);
|
||||
memset(buffer->data + buffer->count, 0, index + 1 - buffer->count);
|
||||
buffer->count = index + 1;
|
||||
}
|
||||
buffer->data[index] = (uint8_t)(janet_unwrap_integer(value) & 0xFF);
|
||||
@@ -771,11 +767,7 @@ void janet_put(Janet ds, Janet key, Janet value) {
|
||||
JanetArray *array = janet_unwrap_array(ds);
|
||||
int32_t index = getter_checkint(type, key, INT32_MAX - 1);
|
||||
if (index >= array->count) {
|
||||
janet_array_ensure(array, index + 1, 2);
|
||||
for (int32_t i = array->count; i < index + 1; i++) {
|
||||
array->data[i] = janet_wrap_nil();
|
||||
}
|
||||
array->count = index + 1;
|
||||
janet_array_setcount(array, index + 1);
|
||||
}
|
||||
array->data[index] = value;
|
||||
break;
|
||||
@@ -786,9 +778,7 @@ void janet_put(Janet ds, Janet key, Janet value) {
|
||||
if (!janet_checkint(value))
|
||||
janet_panicf("can only put integers in buffers, got %v", value);
|
||||
if (index >= buffer->count) {
|
||||
janet_buffer_ensure(buffer, index + 1, 2);
|
||||
memset(buffer->data + buffer->count, 0, index + 1 - buffer->count);
|
||||
buffer->count = index + 1;
|
||||
janet_buffer_setcount(buffer, index + 1);
|
||||
}
|
||||
buffer->data[index] = (uint8_t)(janet_unwrap_integer(value) & 0xFF);
|
||||
break;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
106
src/core/vm.c
106
src/core/vm.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -115,7 +115,7 @@
|
||||
#define vm_maybe_auto_suspend(COND)
|
||||
#else
|
||||
#define vm_maybe_auto_suspend(COND) do { \
|
||||
if ((COND) && janet_atomic_load_relaxed(&janet_vm.auto_suspend)) { \
|
||||
if ((COND) && janet_vm.auto_suspend) { \
|
||||
fiber->flags |= (JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP); \
|
||||
vm_return(JANET_SIGNAL_INTERRUPT, janet_wrap_nil()); \
|
||||
} \
|
||||
@@ -129,9 +129,7 @@
|
||||
if (!janet_checktype(op1, JANET_NUMBER)) {\
|
||||
vm_commit();\
|
||||
Janet _argv[2] = { op1, janet_wrap_number(CS) };\
|
||||
Janet a = janet_mcall(#op, 2, _argv);\
|
||||
stack = fiber->data + fiber->frame;\
|
||||
stack[A] = a;\
|
||||
stack[A] = janet_mcall(#op, 2, _argv);\
|
||||
vm_checkgc_pcnext();\
|
||||
} else {\
|
||||
double x1 = janet_unwrap_number(op1);\
|
||||
@@ -145,9 +143,7 @@
|
||||
if (!janet_checktype(op1, JANET_NUMBER)) {\
|
||||
vm_commit();\
|
||||
Janet _argv[2] = { op1, janet_wrap_number(CS) };\
|
||||
Janet a = janet_mcall(#op, 2, _argv);\
|
||||
stack = fiber->data + fiber->frame;\
|
||||
stack[A] = a;\
|
||||
stack[A] = janet_mcall(#op, 2, _argv);\
|
||||
vm_checkgc_pcnext();\
|
||||
} else {\
|
||||
double y1 = janet_unwrap_number(op1);\
|
||||
@@ -170,9 +166,7 @@
|
||||
vm_pcnext();\
|
||||
} else {\
|
||||
vm_commit();\
|
||||
Janet a = janet_binop_call(#op, "r" #op, op1, op2);\
|
||||
stack = fiber->data + fiber->frame;\
|
||||
stack[A] = a;\
|
||||
stack[A] = janet_binop_call(#op, "r" #op, op1, op2);\
|
||||
vm_checkgc_pcnext();\
|
||||
}\
|
||||
}
|
||||
@@ -192,9 +186,7 @@
|
||||
vm_pcnext();\
|
||||
} else {\
|
||||
vm_commit();\
|
||||
Janet a = janet_binop_call(#op, "r" #op, op1, op2);\
|
||||
stack = fiber->data + fiber->frame;\
|
||||
stack[A] = a;\
|
||||
stack[A] = janet_binop_call(#op, "r" #op, op1, op2);\
|
||||
vm_checkgc_pcnext();\
|
||||
}\
|
||||
}
|
||||
@@ -211,9 +203,7 @@
|
||||
vm_pcnext();\
|
||||
} else {\
|
||||
vm_commit();\
|
||||
Janet a = janet_wrap_boolean(janet_compare(op1, op2) op 0);\
|
||||
stack = fiber->data + fiber->frame;\
|
||||
stack[A] = a;\
|
||||
stack[A] = janet_wrap_boolean(janet_compare(op1, op2) op 0);\
|
||||
vm_checkgc_pcnext();\
|
||||
}\
|
||||
}
|
||||
@@ -227,9 +217,7 @@
|
||||
vm_pcnext();\
|
||||
} else {\
|
||||
vm_commit();\
|
||||
Janet a = janet_wrap_boolean(janet_compare(op1, janet_wrap_integer(CS)) op 0);\
|
||||
stack = fiber->data + fiber->frame;\
|
||||
stack[A] = a;\
|
||||
stack[A] = janet_wrap_boolean(janet_compare(op1, janet_wrap_integer(CS)) op 0);\
|
||||
vm_checkgc_pcnext();\
|
||||
}\
|
||||
}
|
||||
@@ -722,9 +710,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
vm_pcnext();
|
||||
} else {
|
||||
vm_commit();
|
||||
Janet a = janet_binop_call("div", "rdiv", op1, op2);
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[A] = a;
|
||||
stack[A] = janet_binop_call("div", "rdiv", op1, op2);
|
||||
vm_checkgc_pcnext();
|
||||
}
|
||||
}
|
||||
@@ -744,9 +730,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
vm_pcnext();
|
||||
} else {
|
||||
vm_commit();
|
||||
Janet a = janet_binop_call("mod", "rmod", op1, op2);
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[A] = a;
|
||||
stack[A] = janet_binop_call("mod", "rmod", op1, op2);
|
||||
vm_checkgc_pcnext();
|
||||
}
|
||||
}
|
||||
@@ -761,9 +745,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
vm_pcnext();
|
||||
} else {
|
||||
vm_commit();
|
||||
Janet a = janet_binop_call("%", "r%", op1, op2);
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[A] = a;
|
||||
stack[A] = janet_binop_call("%", "r%", op1, op2);
|
||||
vm_checkgc_pcnext();
|
||||
}
|
||||
}
|
||||
@@ -784,9 +766,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
vm_pcnext();
|
||||
} else {
|
||||
vm_commit();
|
||||
Janet a = janet_unary_call("~", op);
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[A] = a;
|
||||
stack[A] = janet_unary_call("~", op);
|
||||
vm_checkgc_pcnext();
|
||||
}
|
||||
}
|
||||
@@ -818,14 +798,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_JUMP)
|
||||
vm_maybe_auto_suspend(DS <= 0);
|
||||
pc += DS;
|
||||
vm_maybe_auto_suspend(DS <= 0);
|
||||
vm_next();
|
||||
|
||||
VM_OP(JOP_JUMP_IF)
|
||||
if (janet_truthy(stack[A])) {
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
pc += ES;
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
} else {
|
||||
pc++;
|
||||
}
|
||||
@@ -835,15 +815,15 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
if (janet_truthy(stack[A])) {
|
||||
pc++;
|
||||
} else {
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
pc += ES;
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
}
|
||||
vm_next();
|
||||
|
||||
VM_OP(JOP_JUMP_IF_NIL)
|
||||
if (janet_checktype(stack[A], JANET_NIL)) {
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
pc += ES;
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
} else {
|
||||
pc++;
|
||||
}
|
||||
@@ -853,8 +833,8 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
if (janet_checktype(stack[A], JANET_NIL)) {
|
||||
pc++;
|
||||
} else {
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
pc += ES;
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
}
|
||||
vm_next();
|
||||
|
||||
@@ -892,11 +872,8 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
stack[A] = janet_wrap_boolean(!janet_checktype(stack[B], JANET_NUMBER) || (janet_unwrap_number(stack[B]) != (double) CS));
|
||||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_COMPARE) {
|
||||
Janet a = janet_wrap_integer(janet_compare(stack[B], stack[C]));
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[A] = a;
|
||||
}
|
||||
VM_OP(JOP_COMPARE)
|
||||
stack[A] = janet_wrap_integer(janet_compare(stack[B], stack[C]));
|
||||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_NEXT)
|
||||
@@ -1127,11 +1104,11 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
}
|
||||
fiber->child = child;
|
||||
JanetSignal sig = janet_continue_no_check(child, stack[C], &retreg);
|
||||
stack = fiber->data + fiber->frame;
|
||||
if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
|
||||
vm_return(sig, retreg);
|
||||
}
|
||||
fiber->child = NULL;
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[A] = retreg;
|
||||
vm_checkgc_pcnext();
|
||||
}
|
||||
@@ -1180,7 +1157,6 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
vm_commit();
|
||||
fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL;
|
||||
janet_put(stack[A], stack[B], stack[C]);
|
||||
stack = fiber->data + fiber->frame;
|
||||
fiber->flags &= ~JANET_FIBER_RESUME_NO_USEVAL;
|
||||
vm_checkgc_pcnext();
|
||||
|
||||
@@ -1188,44 +1164,27 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
vm_commit();
|
||||
fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL;
|
||||
janet_putindex(stack[A], C, stack[B]);
|
||||
stack = fiber->data + fiber->frame;
|
||||
fiber->flags &= ~JANET_FIBER_RESUME_NO_USEVAL;
|
||||
vm_checkgc_pcnext();
|
||||
|
||||
VM_OP(JOP_IN)
|
||||
vm_commit();
|
||||
{
|
||||
Janet a = janet_in(stack[B], stack[C]);
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[A] = a;
|
||||
}
|
||||
stack[A] = janet_in(stack[B], stack[C]);
|
||||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_GET)
|
||||
vm_commit();
|
||||
{
|
||||
Janet a = janet_get(stack[B], stack[C]);
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[A] = a;
|
||||
}
|
||||
stack[A] = janet_get(stack[B], stack[C]);
|
||||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_GET_INDEX)
|
||||
vm_commit();
|
||||
{
|
||||
Janet a = janet_getindex(stack[B], C);
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[A] = a;
|
||||
}
|
||||
stack[A] = janet_getindex(stack[B], C);
|
||||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_LENGTH)
|
||||
vm_commit();
|
||||
{
|
||||
Janet a = janet_lengthv(stack[E]);
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[A] = a;
|
||||
}
|
||||
stack[A] = janet_lengthv(stack[E]);
|
||||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_MAKE_ARRAY) {
|
||||
@@ -1429,11 +1388,6 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
|
||||
|
||||
if (signal != JANET_SIGNAL_OK) {
|
||||
/* Should match logic in janet_signalv */
|
||||
#ifdef JANET_EV
|
||||
if (janet_vm.root_fiber != NULL && signal == JANET_SIGNAL_EVENT) {
|
||||
janet_vm.root_fiber->sched_id++;
|
||||
}
|
||||
#endif
|
||||
if (signal != JANET_SIGNAL_ERROR) {
|
||||
*janet_vm.return_reg = janet_wrap_string(janet_formatc("%v coerced from %s to error", *janet_vm.return_reg, janet_signal_names[signal]));
|
||||
}
|
||||
@@ -1559,15 +1513,6 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is a nested continue (root_fiber already set), root the fiber
|
||||
* so it survives GC. janet_collect only marks root_fiber, so without
|
||||
* this a nested fiber (e.g., from janet_pcall in a C function) would be
|
||||
* invisible to GC and could be collected while actively running. */
|
||||
int fiber_rooted = (janet_vm.root_fiber != NULL);
|
||||
if (fiber_rooted) {
|
||||
janet_gcroot(janet_wrap_fiber(fiber));
|
||||
}
|
||||
|
||||
/* Save global state */
|
||||
JanetTryState tstate;
|
||||
JanetSignal sig = janet_try(&tstate);
|
||||
@@ -1583,9 +1528,6 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
|
||||
if (janet_vm.root_fiber == fiber) janet_vm.root_fiber = NULL;
|
||||
janet_fiber_set_status(fiber, sig);
|
||||
janet_restore(&tstate);
|
||||
if (fiber_rooted) {
|
||||
janet_gcunroot(janet_wrap_fiber(fiber));
|
||||
}
|
||||
fiber->last_value = tstate.payload;
|
||||
*out = tstate.payload;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -194,18 +194,12 @@ Janet janet_wrap_number_safe(double d) {
|
||||
|
||||
void *janet_nanbox_to_pointer(Janet x) {
|
||||
x.i64 &= JANET_NANBOX_PAYLOADBITS;
|
||||
x.u64 <<= JANET_NANBOX_64_POINTER_SHIFT; /* Alignment, usually 0 */
|
||||
return x.pointer;
|
||||
}
|
||||
|
||||
Janet janet_nanbox_from_pointer(void *p, uint64_t tagmask) {
|
||||
Janet ret;
|
||||
ret.pointer = p;
|
||||
/* Should be noop when pointer shift is 0 */
|
||||
/*
|
||||
janet_assert(!(ret.u64 & (uint64_t) ((1 << JANET_NANBOX_64_POINTER_SHIFT) - 1)), "unaligned pointer wrap");
|
||||
*/
|
||||
ret.u64 >>= JANET_NANBOX_64_POINTER_SHIFT; /* Alignment, usually 0 */
|
||||
ret.u64 |= tagmask;
|
||||
return ret;
|
||||
}
|
||||
@@ -213,11 +207,6 @@ Janet janet_nanbox_from_pointer(void *p, uint64_t tagmask) {
|
||||
Janet janet_nanbox_from_cpointer(const void *p, uint64_t tagmask) {
|
||||
Janet ret;
|
||||
ret.pointer = (void *)p;
|
||||
/* Should be noop when pointer shift is 0 */
|
||||
/*
|
||||
janet_assert(!(ret.u64 & (uint64_t) ((1 << JANET_NANBOX_64_POINTER_SHIFT) - 1)), "unaligned pointer wrap");
|
||||
*/
|
||||
ret.u64 >>= JANET_NANBOX_64_POINTER_SHIFT; /* Alignment, usually 0 */
|
||||
ret.u64 |= tagmask;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -67,21 +67,11 @@ extern "C" {
|
||||
#define JANET_LINUX 1
|
||||
#endif
|
||||
|
||||
/* Check for Android */
|
||||
#ifdef __ANDROID__
|
||||
#define JANET_ANDROID 1
|
||||
#endif
|
||||
|
||||
/* Check for Cygwin */
|
||||
#if defined(__CYGWIN__)
|
||||
#define JANET_CYGWIN 1
|
||||
#endif
|
||||
|
||||
/* Check for Illumos */
|
||||
#if defined(__illumos__)
|
||||
#define JANET_ILLUMOS 1
|
||||
#endif
|
||||
|
||||
/* Check Unix */
|
||||
#if defined(_AIX) \
|
||||
|| defined(__APPLE__) /* Darwin */ \
|
||||
@@ -121,7 +111,6 @@ extern "C" {
|
||||
|| (defined(__sparc__) && defined(__arch64__) || defined (__sparcv9)) /* BE */ \
|
||||
|| defined(__s390x__) /* S390 64-bit (BE) */ \
|
||||
|| (defined(__ppc64__) || defined(__PPC64__)) \
|
||||
|| defined(PLAN9_arm64) || defined(PLAN9_amd64) \
|
||||
|| defined(__aarch64__) /* ARM 64-bit */ \
|
||||
|| (defined(__riscv) && (__riscv_xlen == 64)) /* RISC-V 64-bit */ \
|
||||
|| defined(__loongarch64) /* LoongArch64 64-bit */
|
||||
@@ -148,7 +137,6 @@ extern "C" {
|
||||
|| defined(__s390x__) /* S390 64-bit */ \
|
||||
|| defined(__s390__) /* S390 32-bit */ \
|
||||
|| defined(__ARMEB__) /* ARM big endian */ \
|
||||
|| defined(__AARCH64EB__) /* ARM64 big endian */ \
|
||||
|| ((defined(__CC_ARM) || defined(__ARMCC__)) /* ARM RealView compiler */ \
|
||||
&& defined(__BIG_ENDIAN))
|
||||
#define JANET_BIG_ENDIAN 1
|
||||
@@ -169,7 +157,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* Check sun */
|
||||
#if defined(__sun) && !defined(JANET_ILLUMOS)
|
||||
#ifdef __sun
|
||||
#define JANET_NO_UTC_MKTIME
|
||||
#endif
|
||||
|
||||
@@ -177,12 +165,14 @@ extern "C" {
|
||||
/* Also enable the thread library only if not single-threaded */
|
||||
#ifdef JANET_SINGLE_THREADED
|
||||
#define JANET_THREAD_LOCAL
|
||||
#elif !(defined(JANET_THREAD_LOCAL)) && defined(__GNUC__)
|
||||
#undef JANET_THREADS
|
||||
#elif defined(__GNUC__)
|
||||
#define JANET_THREAD_LOCAL __thread
|
||||
#elif !(defined(JANET_THREAD_LOCAL)) && defined(_MSC_BUILD)
|
||||
#elif defined(_MSC_BUILD)
|
||||
#define JANET_THREAD_LOCAL __declspec(thread)
|
||||
#elif !(defined(JANET_THREAD_LOCAL))
|
||||
#else
|
||||
#define JANET_THREAD_LOCAL
|
||||
#undef JANET_THREADS
|
||||
#endif
|
||||
|
||||
/* Enable or disable dynamic module loading. Enabled by default. */
|
||||
@@ -307,38 +297,25 @@ extern "C" {
|
||||
* architectures (Nanboxing only tested on x86 and x64), comment out
|
||||
* the JANET_NANBOX define.*/
|
||||
|
||||
#if defined(_M_ARM64) || defined(_M_ARM) || defined(__aarch64__)
|
||||
#define JANET_NO_NANBOX
|
||||
#endif
|
||||
|
||||
#ifndef JANET_NO_NANBOX
|
||||
#ifdef JANET_32
|
||||
#define JANET_NANBOX_32
|
||||
#elif defined(__x86_64__) || defined(_WIN64) || defined(__riscv) || defined(__aarch64__) || defined(_M_ARM64)
|
||||
#elif defined(__x86_64__) || defined(_WIN64) || defined(__riscv)
|
||||
/* We will only enable nanboxing by default on 64 bit systems
|
||||
* for x64, risc-v, and arm64. This is mainly because the approach is tied to the
|
||||
* for x64 and risc-v. This is mainly because the approach is tied to the
|
||||
* implicit 47 bit address space. Many arches allow/require this, but not all,
|
||||
* and it requires cooperation from the OS. ARM should also work in many configurations by taking advantage
|
||||
* of pointer alignment to allow for 48 or 49 bits of address space. */
|
||||
* and it requires cooperation from the OS. ARM should also work in many configurations. */
|
||||
#define JANET_NANBOX_64
|
||||
|
||||
/* Allow 64-bit nanboxing to assume aligned pointers to get back some extra bits for representation.
|
||||
* This is needed to use nanboxing on systems with larger than 47-bit address spaces, such as many
|
||||
* aarch64 systems. */
|
||||
#ifndef JANET_NANBOX_64_POINTER_SHIFT
|
||||
#if (defined(_M_ARM64) || defined(__aarch64__)) && !defined(JANET_APPLE)
|
||||
/* All pointers, including function pointers, should be 4-byte aligned on aarch64 by default.
|
||||
* The exception is aarch64 macos, as it uses the same 47-bit userland address-space as on amd64. */
|
||||
#define JANET_NANBOX_64_POINTER_SHIFT 0 /* TODO - set me back to 2! (trying to trigger crash) */
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Allow for custom pointer alignment as well */
|
||||
#if defined(JANET_NANBOX_64) && !defined(JANET_NANBOX_64_POINTER_SHIFT)
|
||||
#define JANET_NANBOX_64_POINTER_SHIFT 0
|
||||
#endif
|
||||
|
||||
/* Runtime config constants */
|
||||
#ifdef JANET_NO_NANBOX
|
||||
#define JANET_NANBOX_BIT 0x0
|
||||
#define JANET_NANBOX_BIT 0
|
||||
#else
|
||||
#define JANET_NANBOX_BIT 0x1
|
||||
#endif
|
||||
@@ -349,16 +326,9 @@ extern "C" {
|
||||
#define JANET_SINGLE_THREADED_BIT 0
|
||||
#endif
|
||||
|
||||
#ifdef JANET_NANBOX_64_POINTER_SHIFT
|
||||
#define JANET_NANBOX_POINTER_SHIFT_BITS (JANET_NANBOX_64_POINTER_SHIFT ? (0x4 << JANET_NANBOX_64_POINTER_SHIFT) : 0)
|
||||
#else
|
||||
#define JANET_NANBOX_POINTER_SHIFT_BITS 0
|
||||
#endif
|
||||
|
||||
#define JANET_CURRENT_CONFIG_BITS \
|
||||
(JANET_SINGLE_THREADED_BIT | \
|
||||
JANET_NANBOX_BIT | \
|
||||
JANET_NANBOX_POINTER_SHIFT_BITS)
|
||||
JANET_NANBOX_BIT)
|
||||
|
||||
/* Represents the settings used to compile Janet, as well as the version */
|
||||
typedef struct {
|
||||
@@ -621,7 +591,6 @@ typedef void *JanetAbstract;
|
||||
#define JANET_STREAM_WRITABLE 0x400
|
||||
#define JANET_STREAM_ACCEPTABLE 0x800
|
||||
#define JANET_STREAM_UDPSERVER 0x1000
|
||||
#define JANET_STREAM_NOT_CLOSEABLE 0x2000
|
||||
#define JANET_STREAM_TOCLOSE 0x10000
|
||||
|
||||
typedef enum {
|
||||
@@ -688,15 +657,12 @@ JANET_API void janet_stream_level_triggered(JanetStream *stream);
|
||||
* signals. Define them here */
|
||||
#ifdef JANET_WINDOWS
|
||||
typedef long JanetAtomicInt;
|
||||
#elif defined(JANET_PLAN9)
|
||||
typedef long JanetAtomicInt;
|
||||
#else
|
||||
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);
|
||||
JANET_API JanetAtomicInt janet_atomic_load_relaxed(JanetAtomicInt volatile *x);
|
||||
|
||||
/* We provide three possible implementations of Janets. The preferred
|
||||
* nanboxing approach, for 32 or 64 bits, and the standard C version. Code in the rest of the
|
||||
@@ -1094,7 +1060,6 @@ struct JanetAbstractHead {
|
||||
#define JANET_FUNCDEF_FLAG_HASSOURCEMAP 0x800000
|
||||
#define JANET_FUNCDEF_FLAG_STRUCTARG 0x1000000
|
||||
#define JANET_FUNCDEF_FLAG_HASCLOBITSET 0x2000000
|
||||
#define JANET_FUNCDEF_FLAG_NAMEDARGS 0x4000000
|
||||
#define JANET_FUNCDEF_FLAG_TAG 0xFFFF
|
||||
|
||||
/* Source mapping structure for a bytecode instruction */
|
||||
@@ -1136,7 +1101,6 @@ struct JanetFuncDef {
|
||||
int32_t environments_length;
|
||||
int32_t defs_length;
|
||||
int32_t symbolmap_length;
|
||||
int32_t named_args_count;
|
||||
};
|
||||
|
||||
/* A function environment */
|
||||
@@ -1160,21 +1124,9 @@ struct JanetFunction {
|
||||
JanetFuncEnv *envs[];
|
||||
};
|
||||
|
||||
/* Use to read Janet data structures into memory from source code */
|
||||
typedef struct JanetParseState JanetParseState;
|
||||
typedef struct JanetParser JanetParser;
|
||||
|
||||
typedef int (*Consumer)(JanetParser *p, JanetParseState *state, uint8_t c);
|
||||
|
||||
struct JanetParseState {
|
||||
int32_t counter;
|
||||
int32_t argn;
|
||||
int flags;
|
||||
size_t line;
|
||||
size_t column;
|
||||
Consumer consumer;
|
||||
};
|
||||
|
||||
enum JanetParserStatus {
|
||||
JANET_PARSE_ROOT,
|
||||
JANET_PARSE_ERROR,
|
||||
@@ -1210,10 +1162,7 @@ typedef struct {
|
||||
const JanetAbstractType *at;
|
||||
} JanetMarshalContext;
|
||||
|
||||
/* Defines an abstract type. Use a const pointer to one of these structures
|
||||
* when creating abstract types. The memory for this pointer should not be free
|
||||
* until after janet_deinit is called. Usually, this means declaring JanetAbstractType's
|
||||
* as const data at file scope, and creating instances with janet_abstract(&MyType, sizeof(MyTypeStruct)); */
|
||||
/* Defines an abstract type */
|
||||
struct JanetAbstractType {
|
||||
const char *name;
|
||||
int (*gc)(void *data, size_t len);
|
||||
@@ -1229,7 +1178,6 @@ struct JanetAbstractType {
|
||||
Janet(*call)(void *p, int32_t argc, Janet *argv);
|
||||
size_t (*length)(void *p, size_t len);
|
||||
JanetByteView(*bytes)(void *p, size_t len);
|
||||
int (*gcperthread)(void *data, size_t len);
|
||||
};
|
||||
|
||||
/* Some macros to let us add extra types to JanetAbstract types without
|
||||
@@ -1249,8 +1197,7 @@ struct JanetAbstractType {
|
||||
#define JANET_ATEND_NEXT NULL,JANET_ATEND_CALL
|
||||
#define JANET_ATEND_CALL NULL,JANET_ATEND_LENGTH
|
||||
#define JANET_ATEND_LENGTH NULL,JANET_ATEND_BYTES
|
||||
#define JANET_ATEND_BYTES NULL,JANET_ATEND_GCPERTHREAD
|
||||
#define JANET_ATEND_GCPERTHREAD
|
||||
#define JANET_ATEND_BYTES
|
||||
|
||||
struct JanetReg {
|
||||
const char *name;
|
||||
@@ -1301,7 +1248,6 @@ typedef struct JanetFile JanetFile;
|
||||
struct JanetFile {
|
||||
FILE *file;
|
||||
int32_t flags;
|
||||
size_t vbufsize;
|
||||
};
|
||||
|
||||
/* For janet_try and janet_restore */
|
||||
@@ -1435,7 +1381,7 @@ enum JanetOpCode {
|
||||
};
|
||||
|
||||
/* Info about all instructions */
|
||||
extern const enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT];
|
||||
extern enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT];
|
||||
|
||||
/***** END SECTION OPCODES *****/
|
||||
|
||||
@@ -1466,7 +1412,6 @@ JANET_API void janet_loop(void);
|
||||
* } else {
|
||||
* janet_schedule(interrupted_fiber, janet_wrap_nil());
|
||||
* }
|
||||
* janet_interpreter_interrupt_handled(NULL);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
@@ -1506,27 +1451,18 @@ JANET_API void janet_ev_dec_refcount(void);
|
||||
JANET_API void *janet_abstract_begin_threaded(const JanetAbstractType *atype, size_t size);
|
||||
JANET_API void *janet_abstract_end_threaded(void *x);
|
||||
JANET_API void *janet_abstract_threaded(const JanetAbstractType *atype, size_t size);
|
||||
|
||||
/* Allow reference counting on threaded abstract types. This is useful when external code , either
|
||||
* in the current OS thread or in a different OS thread, takes a pointer to this abstract type. The programmer
|
||||
* should tncrement the reference count when taking the pointer, and then decrement and possibly cleanup and free
|
||||
* if the reference count is 0. */
|
||||
JANET_API int32_t janet_abstract_incref(void *abst);
|
||||
JANET_API int32_t janet_abstract_decref(void *abst);
|
||||
|
||||
/* If this returns 0, *abst will be deinitialized and freed. Useful shorthand if there is no other cleanup for
|
||||
* this abstract type before calling `janet_free` on it's backing memory. */
|
||||
JANET_API int32_t janet_abstract_decref_maybe_free(void *abst);
|
||||
|
||||
/* Expose channel utilities */
|
||||
JANET_API JanetChannel *janet_channel_make(uint32_t limit);
|
||||
JANET_API JanetChannel *janet_channel_make_threaded(uint32_t limit);
|
||||
JANET_API JanetChannel *janet_getchannel(const Janet *argv, int32_t n);
|
||||
JANET_API JanetChannel *janet_optchannel(const Janet *argv, int32_t argc, int32_t n, JanetChannel *dflt);
|
||||
JanetChannel *janet_channel_make(uint32_t limit);
|
||||
JanetChannel *janet_channel_make_threaded(uint32_t limit);
|
||||
JanetChannel *janet_getchannel(const Janet *argv, int32_t n);
|
||||
JanetChannel *janet_optchannel(const Janet *argv, int32_t argc, int32_t n, JanetChannel *dflt);
|
||||
JANET_API int janet_channel_give(JanetChannel *channel, Janet x);
|
||||
JANET_API int janet_channel_take(JanetChannel *channel, Janet *out);
|
||||
|
||||
/* Expose some OS sync primitives - mutexes and reader-writer locks */
|
||||
/* Expose some OS sync primitives */
|
||||
JANET_API size_t janet_os_mutex_size(void);
|
||||
JANET_API size_t janet_os_rwlock_size(void);
|
||||
JANET_API void janet_os_mutex_init(JanetOSMutex *mutex);
|
||||
@@ -1594,8 +1530,7 @@ JANET_API void janet_ev_post_event(JanetVM *vm, JanetCallback cb, JanetEVGeneric
|
||||
/* Callback used by janet_ev_threaded_await */
|
||||
JANET_API void janet_ev_default_threaded_callback(JanetEVGenericMessage return_value);
|
||||
|
||||
/* Read async from a stream. These function yield to the event-loop with janet_await(), and so do not return.
|
||||
* When the fiber is resumed, the fiber will simply continue to the next Janet abstract machine instruction. */
|
||||
/* Read async from a stream */
|
||||
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
|
||||
@@ -1604,8 +1539,7 @@ JANET_NO_RETURN JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuff
|
||||
JANET_NO_RETURN JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
|
||||
#endif
|
||||
|
||||
/* Write async to a stream. These function yield to the event-loop with janet_await(), and so do not return.
|
||||
* When the fiber is resumed, the fiber will simply continue to the next Janet abstract machine instruction. */
|
||||
/* Write async to a stream */
|
||||
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
|
||||
@@ -1617,63 +1551,17 @@ JANET_NO_RETURN JANET_API void janet_ev_sendto_string(JanetStream *stream, Janet
|
||||
|
||||
#endif
|
||||
|
||||
/* Parsing.
|
||||
*
|
||||
* E.g.
|
||||
*
|
||||
* JanetParser parser;
|
||||
* janet_parser_init(&parser);
|
||||
* for (int i = 0; i < source_code_length + 1; i++) {
|
||||
* if (i >= source_code_length) {
|
||||
* janet_parser_eof(&parser);
|
||||
* } else {
|
||||
* janet_parser_consume(&parser, source_code[i]);
|
||||
* }
|
||||
* while (janet_parser_has_more(&parser)) {
|
||||
* Janet x = janet_parser_produce(&parser);
|
||||
* janet_printf("got value: %v\n", x);
|
||||
* }
|
||||
* switch (janet_parser_status(&parser)) {
|
||||
* case JANET_PARSE_PENDING: break;
|
||||
* case JANET_PARSE_ERROR: janet_eprintf("error: %s\n", janet_parser_error(&parser)); break;
|
||||
* case JANET_PARSE_ROOT: break;
|
||||
* case JANET_PARSE_DEAD: break;
|
||||
* }
|
||||
* }
|
||||
* janet_parser_deinit(&parser);
|
||||
*
|
||||
* */
|
||||
/* Parsing */
|
||||
extern JANET_API const JanetAbstractType janet_parser_type;
|
||||
|
||||
/* Construct/destruct a parser. Parsers can be allocated on the stack or the heap. */
|
||||
JANET_API void janet_parser_init(JanetParser *parser);
|
||||
JANET_API void janet_parser_deinit(JanetParser *parser);
|
||||
|
||||
/* Feed bytes into the parser. Check the parser state after every byte to handle errors. */
|
||||
JANET_API void janet_parser_consume(JanetParser *parser, uint8_t c);
|
||||
|
||||
/* Check the current status of the parser */
|
||||
JANET_API enum JanetParserStatus janet_parser_status(JanetParser *parser);
|
||||
|
||||
/* Produce a value from the parser. Call this when janet_parser_has_more(&parser) is non-zero. */
|
||||
JANET_API Janet janet_parser_produce(JanetParser *parser);
|
||||
|
||||
/* Produce a value from the parser, wrapped in a tuple. The tuple is used to carry the source mapping information of the
|
||||
* top level form, such as a line number or symbol. */
|
||||
JANET_API Janet janet_parser_produce_wrapped(JanetParser *parser);
|
||||
|
||||
/* When there is an error while parsing (janet_parser_status(&parser) == JANET_PARSE_ERROR), get a nice error string.
|
||||
* Calling this will also flush the parser. */
|
||||
JANET_API const char *janet_parser_error(JanetParser *parser);
|
||||
|
||||
/* If there is a parsing error, flush the parser to set the state back to empty.
|
||||
* This allows for better error recover and less confusing error messages on bad syntax deep inside nested data structures. */
|
||||
JANET_API void janet_parser_flush(JanetParser *parser);
|
||||
|
||||
/* Indicate that there is no more source code */
|
||||
JANET_API void janet_parser_eof(JanetParser *parser);
|
||||
|
||||
/* If non-zero, the parser has values ready to be produced. */
|
||||
JANET_API int janet_parser_has_more(JanetParser *parser);
|
||||
|
||||
/* Assembly */
|
||||
@@ -1717,13 +1605,7 @@ JANET_API JanetCompileResult janet_compile_lint(
|
||||
JANET_API JanetTable *janet_core_env(JanetTable *replacements);
|
||||
JANET_API JanetTable *janet_core_lookup_table(JanetTable *replacements);
|
||||
|
||||
/* Execute strings.
|
||||
*
|
||||
* These functions wrap parsing, compilation, and evalutation into convenient functions.
|
||||
* */
|
||||
#define JANET_DO_ERROR_RUNTIME 0x01
|
||||
#define JANET_DO_ERROR_COMPILE 0x02
|
||||
#define JANET_DO_ERROR_PARSE 0x04
|
||||
/* Execute strings */
|
||||
JANET_API int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out);
|
||||
JANET_API int janet_dostring(JanetTable *env, const char *str, const char *sourcePath, Janet *out);
|
||||
|
||||
@@ -1914,41 +1796,21 @@ JANET_API JanetTable *janet_env_lookup(JanetTable *env);
|
||||
JANET_API void janet_env_lookup_into(JanetTable *renv, JanetTable *env, const char *prefix, int recurse);
|
||||
|
||||
/* GC */
|
||||
|
||||
/* The main interface to garbage collection. Call this to do a full mark and sweep cleanup. */
|
||||
JANET_API void janet_collect(void);
|
||||
|
||||
/* Add "roots" to the garbage collector to prevent the runtime from freeing objects.
|
||||
* This is only needed if code outside of Janet keeps references to Janet values */
|
||||
JANET_API void janet_gcroot(Janet root);
|
||||
JANET_API int janet_gcunroot(Janet root);
|
||||
|
||||
/* Allow disabling garbage collection temporarily or for certain sections of code.
|
||||
* this is a very cheap operation. */
|
||||
JANET_API int janet_gclock(void);
|
||||
JANET_API void janet_gcunlock(int handle);
|
||||
|
||||
/* The mark and sweep components of the mark and sweep collector. Prefer using janet_collect directly. */
|
||||
JANET_API void janet_mark(Janet x);
|
||||
JANET_API void janet_sweep(void);
|
||||
|
||||
/* Clear all gced memory and call all destructors. Used as part of the standard cleanup routune, most programmers will not need this. */
|
||||
JANET_API void janet_collect(void);
|
||||
JANET_API void janet_clear_memory(void);
|
||||
|
||||
/* Remove all GC roots. Used as part of the standard cleanup routine, most programmers will not need this. */
|
||||
JANET_API void janet_gcroot(Janet root);
|
||||
JANET_API int janet_gcunroot(Janet root);
|
||||
JANET_API int janet_gcunrootall(Janet root);
|
||||
|
||||
/* Hint to the collector that memory of size s was just allocated to help it better understand when to free memory. */
|
||||
JANET_API int janet_gclock(void);
|
||||
JANET_API void janet_gcunlock(int handle);
|
||||
JANET_API void janet_gcpressure(size_t s);
|
||||
|
||||
/* Functions */
|
||||
JANET_API JanetFuncDef *janet_funcdef_alloc(void);
|
||||
JANET_API JanetFunction *janet_thunk(JanetFuncDef *def);
|
||||
|
||||
/* Get a function that when called with no args, will return x. */
|
||||
JANET_API JanetFunction *janet_thunk_delay(Janet x);
|
||||
|
||||
/* Do some simple verfification on constructed bytecode to disallow any trivial incorrect bytecode. */
|
||||
JANET_API int janet_verify(JanetFuncDef *def);
|
||||
|
||||
/* Pretty printing */
|
||||
@@ -1997,7 +1859,7 @@ JANET_API void janet_vm_free(JanetVM *vm);
|
||||
JANET_API void janet_vm_save(JanetVM *into);
|
||||
JANET_API void janet_vm_load(JanetVM *from);
|
||||
JANET_API void janet_interpreter_interrupt(JanetVM *vm);
|
||||
JANET_API void janet_interpreter_interrupt_handled(JanetVM *vm); /* Call this after running interrupt handler */
|
||||
JANET_API void janet_interpreter_interrupt_handled(JanetVM *vm);
|
||||
JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out);
|
||||
JANET_API JanetSignal janet_continue_signal(JanetFiber *fiber, Janet in, Janet *out, JanetSignal sig);
|
||||
JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f);
|
||||
@@ -2022,14 +1884,9 @@ JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *pr
|
||||
#define JANET_SANDBOX_FFI_USE 2048
|
||||
#define JANET_SANDBOX_FFI_JIT 4096
|
||||
#define JANET_SANDBOX_SIGNAL 8192
|
||||
#define JANET_SANDBOX_CHROOT 16384
|
||||
#define JANET_SANDBOX_FFI (JANET_SANDBOX_FFI_DEFINE | JANET_SANDBOX_FFI_USE | JANET_SANDBOX_FFI_JIT)
|
||||
#define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP)
|
||||
#define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN)
|
||||
#define JANET_SANDBOX_COMPILE 32768
|
||||
#define JANET_SANDBOX_ASM 65536
|
||||
#define JANET_SANDBOX_THREADS 131072
|
||||
#define JANET_SANDBOX_UNMARSHAL 262144
|
||||
#define JANET_SANDBOX_ALL (UINT32_MAX)
|
||||
JANET_API void janet_sandbox(uint32_t flags);
|
||||
JANET_API void janet_sandbox_assert(uint32_t forbidden_flags);
|
||||
@@ -2074,23 +1931,10 @@ JANET_API JanetBinding janet_resolve_ext(JanetTable *env, JanetSymbol sym);
|
||||
/* Get values from the core environment. */
|
||||
JANET_API Janet janet_resolve_core(const char *name);
|
||||
|
||||
/* New C API
|
||||
*
|
||||
* The "New" C API is intended to make constructing good documentation and source maps
|
||||
* much more straightforward. This not only ensures doc strings for functions in native
|
||||
* modules, it also add source code mapping for C functions so that programmers can see which
|
||||
* file and line a native function that calls janet_panic came from.
|
||||
*
|
||||
* */
|
||||
|
||||
#if defined(JANET_NANBOX_64) && (JANET_NANBOX_64_POINTER_SHIFT != 0) && !defined(JANET_MSVC)
|
||||
#define JANET_CFUNCTION_ALIGN __attribute__((aligned(1 << JANET_NANBOX_64_POINTER_SHIFT)))
|
||||
#else
|
||||
#define JANET_CFUNCTION_ALIGN
|
||||
#endif
|
||||
/* New C API */
|
||||
|
||||
/* Shorthand for janet C function declarations */
|
||||
#define JANET_CFUN(name) JANET_CFUNCTION_ALIGN Janet name (int32_t argc, Janet *argv)
|
||||
#define JANET_CFUN(name) Janet name (int32_t argc, Janet *argv)
|
||||
|
||||
/* Declare a C function with documentation and source mapping */
|
||||
#define JANET_REG_END {NULL, NULL, NULL, NULL, 0}
|
||||
@@ -2106,7 +1950,7 @@ JANET_API Janet janet_resolve_core(const char *name);
|
||||
#define JANET_REG_S(JNAME, CNAME) {JNAME, CNAME, NULL, __FILE__, CNAME##_sourceline_}
|
||||
#define JANET_FN_S(CNAME, USAGE, DOCSTRING) \
|
||||
static const int32_t CNAME##_sourceline_ = __LINE__; \
|
||||
Janet JANET_CFUNCTION_ALIGN CNAME (int32_t argc, Janet *argv)
|
||||
Janet CNAME (int32_t argc, Janet *argv)
|
||||
#define JANET_DEF_S(ENV, JNAME, VAL, DOC) \
|
||||
janet_def_sm(ENV, JNAME, VAL, NULL, __FILE__, __LINE__)
|
||||
|
||||
@@ -2114,7 +1958,7 @@ JANET_API Janet janet_resolve_core(const char *name);
|
||||
#define JANET_REG_D(JNAME, CNAME) {JNAME, CNAME, CNAME##_docstring_, NULL, 0}
|
||||
#define JANET_FN_D(CNAME, USAGE, DOCSTRING) \
|
||||
static const char CNAME##_docstring_[] = USAGE "\n\n" DOCSTRING; \
|
||||
Janet JANET_CFUNCTION_ALIGN CNAME (int32_t argc, Janet *argv)
|
||||
Janet CNAME (int32_t argc, Janet *argv)
|
||||
#define JANET_DEF_D(ENV, JNAME, VAL, DOC) \
|
||||
janet_def(ENV, JNAME, VAL, DOC)
|
||||
|
||||
@@ -2123,7 +1967,7 @@ JANET_API Janet janet_resolve_core(const char *name);
|
||||
#define JANET_FN_SD(CNAME, USAGE, DOCSTRING) \
|
||||
static const int32_t CNAME##_sourceline_ = __LINE__; \
|
||||
static const char CNAME##_docstring_[] = USAGE "\n\n" DOCSTRING; \
|
||||
Janet JANET_CFUNCTION_ALIGN CNAME (int32_t argc, Janet *argv)
|
||||
Janet CNAME (int32_t argc, Janet *argv)
|
||||
#define JANET_DEF_SD(ENV, JNAME, VAL, DOC) \
|
||||
janet_def_sm(ENV, JNAME, VAL, DOC, __FILE__, __LINE__)
|
||||
|
||||
@@ -2242,8 +2086,6 @@ JANET_API int32_t janet_optinteger(const Janet *argv, int32_t argc, int32_t n, i
|
||||
JANET_API int64_t janet_optinteger64(const Janet *argv, int32_t argc, int32_t n, int64_t dflt);
|
||||
JANET_API size_t janet_optsize(const Janet *argv, int32_t argc, int32_t n, size_t dflt);
|
||||
JANET_API JanetAbstract janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetAbstractType *at, JanetAbstract dflt);
|
||||
JANET_API uint32_t janet_optuinteger(const Janet *argv, int32_t argc, int32_t n, uint32_t dflt);
|
||||
JANET_API uint64_t janet_optuinteger64(const Janet *argv, int32_t argc, int32_t n, uint64_t dflt);
|
||||
|
||||
/* Mutable optional types specify a size default, and construct a new value if none is provided */
|
||||
JANET_API JanetBuffer *janet_optbuffer(const Janet *argv, int32_t argc, int32_t n, int32_t dflt_len);
|
||||
@@ -2340,13 +2182,10 @@ typedef enum {
|
||||
RULE_UNREF, /* [rule, tag] */
|
||||
RULE_CAPTURE_NUM, /* [rule, tag] */
|
||||
RULE_SUB, /* [rule, rule] */
|
||||
RULE_TIL, /* [rule, rule] */
|
||||
RULE_SPLIT, /* [rule, rule] */
|
||||
RULE_NTH, /* [nth, rule, tag] */
|
||||
RULE_ONLY_TAGS, /* [rule] */
|
||||
RULE_MATCHSPLICE, /* [rule, constant, tag] */
|
||||
RULE_DEBUG, /* [] */
|
||||
} JanetPegOpcode;
|
||||
} JanetPegOpcod;
|
||||
|
||||
typedef struct {
|
||||
uint32_t *bytecode;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include <janet.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@@ -80,11 +79,9 @@ static void simpleline(JanetBuffer *buffer) {
|
||||
int c;
|
||||
for (;;) {
|
||||
c = fgetc(in);
|
||||
#ifndef JANET_PLAN9
|
||||
if (c < 0 && !feof(in) && errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (feof(in) || c < 0) {
|
||||
break;
|
||||
}
|
||||
@@ -113,8 +110,6 @@ static JANET_THREAD_LOCAL int gbl_historyi = 0;
|
||||
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
|
||||
static JANET_THREAD_LOCAL int gbl_match_count = 0;
|
||||
static JANET_THREAD_LOCAL int gbl_lines_below = 0;
|
||||
static JANET_THREAD_LOCAL int gbl_history_loaded = 0;
|
||||
static JANET_THREAD_LOCAL char *gbl_history_file = NULL;
|
||||
#endif
|
||||
|
||||
/* Fallback */
|
||||
@@ -312,9 +307,7 @@ static int curpos(void) {
|
||||
char buf[32];
|
||||
int cols, rows;
|
||||
unsigned int i = 0;
|
||||
#ifndef JANET_PLAN9
|
||||
if (write_console("\x1b[6n", 4) != 4) return -1;
|
||||
#endif
|
||||
while (i < sizeof(buf) - 1) {
|
||||
if (read_console(buf + i, 1) != 1) break;
|
||||
if (buf[i] == 'R') break;
|
||||
@@ -363,52 +356,33 @@ static void clear(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static int getplen(void) {
|
||||
int _plen = gbl_plen;
|
||||
/* Ensure at least 16 characters of data entry; */
|
||||
while (_plen && (_plen + 16 > gbl_cols)) {
|
||||
_plen--;
|
||||
}
|
||||
return _plen;
|
||||
}
|
||||
|
||||
static void refresh(void) {
|
||||
char seq[64];
|
||||
JanetBuffer b;
|
||||
|
||||
/* If prompt is too long, truncate */
|
||||
int _plen = getplen();
|
||||
|
||||
/* Keep cursor position on screen */
|
||||
char *_buf = gbl_buf;
|
||||
int _len = gbl_len;
|
||||
int _pos = gbl_pos;
|
||||
|
||||
while ((_plen + _pos) >= gbl_cols) {
|
||||
while ((gbl_plen + _pos) >= gbl_cols) {
|
||||
_buf++;
|
||||
_len--;
|
||||
_pos--;
|
||||
}
|
||||
|
||||
while ((_plen + _len) > gbl_cols) {
|
||||
while ((gbl_plen + _len) > gbl_cols) {
|
||||
_len--;
|
||||
}
|
||||
|
||||
|
||||
janet_buffer_init(&b, 0);
|
||||
/* Cursor to left edge, gbl_prompt and buffer */
|
||||
janet_buffer_push_u8(&b, '\r');
|
||||
janet_buffer_push_bytes(&b, (const uint8_t *) gbl_prompt, _plen);
|
||||
if (_len > 0) {
|
||||
janet_buffer_push_bytes(&b, (uint8_t *) _buf, _len);
|
||||
}
|
||||
janet_buffer_push_cstring(&b, gbl_prompt);
|
||||
janet_buffer_push_bytes(&b, (uint8_t *) _buf, _len);
|
||||
/* Erase to right */
|
||||
janet_buffer_push_cstring(&b, "\x1b[0K\r");
|
||||
janet_buffer_push_cstring(&b, "\x1b[0K");
|
||||
/* Move cursor to original position. */
|
||||
if (_pos + _plen) {
|
||||
snprintf(seq, 64, "\x1b[%dC", (int)(_pos + _plen));
|
||||
janet_buffer_push_cstring(&b, seq);
|
||||
}
|
||||
snprintf(seq, 64, "\r\x1b[%dC", (int)(_pos + gbl_plen));
|
||||
janet_buffer_push_cstring(&b, seq);
|
||||
if (write_console((char *) b.data, b.count) == -1) {
|
||||
exit(1);
|
||||
}
|
||||
@@ -432,8 +406,7 @@ static int insert(char c, int draw) {
|
||||
gbl_buf[gbl_pos++] = c;
|
||||
gbl_buf[++gbl_len] = '\0';
|
||||
if (draw) {
|
||||
int _plen = getplen();
|
||||
if (_plen + gbl_len < gbl_cols) {
|
||||
if (gbl_plen + gbl_len < gbl_cols) {
|
||||
/* Avoid a full update of the line in the
|
||||
* trivial case. */
|
||||
if (write_console(&c, 1) == -1) return -1;
|
||||
@@ -451,63 +424,6 @@ static int insert(char c, int draw) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void calc_history_file(void) {
|
||||
char *hist = getenv("JANET_HISTFILE");
|
||||
if (hist != NULL) {
|
||||
gbl_history_file = sdup(hist);
|
||||
} else {
|
||||
gbl_history_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void loadhistory(void) {
|
||||
if (gbl_history_loaded) return;
|
||||
calc_history_file();
|
||||
gbl_history_loaded = 1;
|
||||
if (NULL == gbl_history_file) return;
|
||||
FILE *history_file = fopen(gbl_history_file, "rb");
|
||||
if (NULL == history_file) return;
|
||||
JanetParser p;
|
||||
janet_parser_init(&p);
|
||||
int c = 0;
|
||||
while ((c = fgetc(history_file))) {
|
||||
if (c == EOF) {
|
||||
janet_parser_eof(&p);
|
||||
} else {
|
||||
janet_parser_consume(&p, c);
|
||||
}
|
||||
|
||||
while (janet_parser_has_more(&p) && gbl_history_count < JANET_HISTORY_MAX) {
|
||||
if (janet_parser_status(&p) == JANET_PARSE_ERROR) {
|
||||
janet_eprintf("bad history file: %s\n", janet_parser_error(&p));
|
||||
goto parsing_done;
|
||||
}
|
||||
Janet x = janet_parser_produce(&p);
|
||||
const char *cstr = (const char *) janet_to_string(x);
|
||||
if (cstr[0]) { /* Drop empty strings */
|
||||
gbl_history[gbl_history_count++] = sdup(cstr);
|
||||
}
|
||||
}
|
||||
|
||||
if (c == EOF) break;
|
||||
}
|
||||
parsing_done:
|
||||
janet_parser_deinit(&p);
|
||||
gbl_historyi = 0;
|
||||
fclose(history_file);
|
||||
}
|
||||
|
||||
static void savehistory(void) {
|
||||
if (gbl_history_count < 1 || (gbl_history_file == NULL)) return;
|
||||
FILE *history_file = fopen(gbl_history_file, "wb");
|
||||
for (int i = 0; i < gbl_history_count; i++) {
|
||||
if (gbl_history[i][0]) { /* Drop empty strings */
|
||||
janet_dynprintf(NULL, history_file, "%j\n", janet_cstringv(gbl_history[i]));
|
||||
}
|
||||
}
|
||||
fclose(history_file);
|
||||
}
|
||||
|
||||
static void historymove(int delta) {
|
||||
if (gbl_history_count > 1) {
|
||||
janet_free(gbl_history[gbl_historyi]);
|
||||
@@ -944,12 +860,11 @@ static int line() {
|
||||
gbl_len = 0;
|
||||
gbl_pos = 0;
|
||||
while (gbl_prompt[gbl_plen]) gbl_plen++;
|
||||
int _plen = getplen();
|
||||
gbl_buf[0] = '\0';
|
||||
|
||||
addhistory();
|
||||
|
||||
if (write_console((char *) gbl_prompt, _plen) == -1) return -1;
|
||||
if (write_console((char *) gbl_prompt, gbl_plen) == -1) return -1;
|
||||
for (;;) {
|
||||
char c;
|
||||
char seq[5];
|
||||
@@ -975,7 +890,6 @@ static int line() {
|
||||
case 3: /* ctrl-c */
|
||||
clearlines();
|
||||
norawmode();
|
||||
savehistory();
|
||||
#ifdef _WIN32
|
||||
ExitProcess(1);
|
||||
#else
|
||||
@@ -1169,21 +1083,17 @@ void janet_line_init() {
|
||||
}
|
||||
|
||||
void janet_line_deinit() {
|
||||
int i;
|
||||
norawmode();
|
||||
for (int i = 0; i < gbl_history_count; i++)
|
||||
for (i = 0; i < gbl_history_count; i++)
|
||||
janet_free(gbl_history[i]);
|
||||
gbl_historyi = 0;
|
||||
if (gbl_history_file) {
|
||||
janet_free(gbl_history_file);
|
||||
gbl_history_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void janet_line_get(const char *p, JanetBuffer *buffer) {
|
||||
gbl_prompt = p;
|
||||
buffer->count = 0;
|
||||
gbl_historyi = 0;
|
||||
loadhistory();
|
||||
if (check_simpleline(buffer)) return;
|
||||
FILE *out = janet_dynfile("err", stderr);
|
||||
if (line()) {
|
||||
@@ -1219,10 +1129,6 @@ int main(int argc, char **argv) {
|
||||
JanetArray *args;
|
||||
JanetTable *env;
|
||||
|
||||
#ifdef JANET_PLAN9
|
||||
setfcr(0);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
setup_console_output();
|
||||
#endif
|
||||
@@ -1278,10 +1184,6 @@ int main(int argc, char **argv) {
|
||||
status = janet_loop_fiber(fiber);
|
||||
|
||||
/* Deinitialize vm */
|
||||
|
||||
#if !defined(JANET_SIMPLE_GETLINE)
|
||||
savehistory();
|
||||
#endif
|
||||
janet_deinit();
|
||||
janet_line_deinit();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Test that GC does not collect fibers during janet_pcall.
|
||||
*
|
||||
* Bug: janet_collect() marks janet_vm.root_fiber but not janet_vm.fiber.
|
||||
* When janet_pcall is called from a C function, the inner fiber becomes
|
||||
* janet_vm.fiber while root_fiber still points to the outer fiber. If GC
|
||||
* triggers inside the inner fiber's execution, the inner fiber is not in
|
||||
* any GC root set and can be collected — including its stack memory —
|
||||
* while it is actively running.
|
||||
*
|
||||
* Two tests:
|
||||
* 1. Single nesting: F1 -> C func -> janet_pcall -> F2
|
||||
* F2 is not marked (it's janet_vm.fiber but not root_fiber)
|
||||
* 2. Deep nesting: F1 -> C func -> janet_pcall -> F2 -> C func -> janet_pcall -> F3
|
||||
* F2 is not marked (saved only in a C stack local tstate.vm_fiber)
|
||||
*
|
||||
* Build (after building janet):
|
||||
* cc -o build/test-gc-pcall test/test-gc-pcall.c \
|
||||
* -Isrc/include -Isrc/conf build/libjanet.a -lm -lpthread -ldl
|
||||
*
|
||||
* Run:
|
||||
* ./build/test-gc-pcall
|
||||
*/
|
||||
|
||||
#include "janet.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/* C function that calls a Janet callback via janet_pcall. */
|
||||
static Janet cfun_call_via_pcall(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFunction *fn = janet_getfunction(argv, 0);
|
||||
|
||||
Janet result;
|
||||
JanetFiber *fiber = NULL;
|
||||
JanetSignal sig = janet_pcall(fn, 0, NULL, &result, &fiber);
|
||||
|
||||
if (sig != JANET_SIGNAL_OK) {
|
||||
janet_panicv(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int run_test(JanetTable *env, const char *name, const char *source) {
|
||||
printf(" %s... ", name);
|
||||
fflush(stdout);
|
||||
Janet result;
|
||||
int status = janet_dostring(env, source, name, &result);
|
||||
if (status != 0) {
|
||||
printf("FAIL (crashed or errored)\n");
|
||||
return 1;
|
||||
}
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Test 1: Single nesting.
|
||||
* F1 -> cfun_call_via_pcall -> janet_pcall -> F2
|
||||
* F2 is janet_vm.fiber but not root_fiber, so GC can collect it.
|
||||
*
|
||||
* All allocations are done in Janet code so GC checks trigger in the
|
||||
* VM loop (janet_gcalloc does NOT call janet_collect — only the VM's
|
||||
* vm_checkgc_next does). */
|
||||
static const char test_single[] =
|
||||
"(gcsetinterval 1024)\n"
|
||||
"(def cb\n"
|
||||
" (do\n"
|
||||
" (def captured @{:key \"value\" :nested @[1 2 3 4 5]})\n"
|
||||
" (fn []\n"
|
||||
" (var result nil)\n"
|
||||
" (for i 0 500\n"
|
||||
" (def t @{:i i :s (string \"iter-\" i) :arr @[i (+ i 1) (+ i 2)]})\n"
|
||||
" (set result (get captured :key)))\n"
|
||||
" result)))\n"
|
||||
"(for round 0 200\n"
|
||||
" (def result (call-via-pcall cb))\n"
|
||||
" (assert (= result \"value\")\n"
|
||||
" (string \"round \" round \": expected 'value', got \" (describe result))))\n";
|
||||
|
||||
/* Test 2: Deep nesting.
|
||||
* F1 -> cfun_call_via_pcall -> janet_pcall -> F2 -> cfun_call_via_pcall -> janet_pcall -> F3
|
||||
* F2 is saved only in C stack local tstate.vm_fiber, invisible to GC.
|
||||
* F2's stack data can be freed if F2 is collected during F3's execution.
|
||||
*
|
||||
* The inner callback allocates in Janet code (not C) to ensure the
|
||||
* VM loop triggers GC checks during F3's execution. */
|
||||
static const char test_deep[] =
|
||||
"(gcsetinterval 1024)\n"
|
||||
"(def inner-cb\n"
|
||||
" (do\n"
|
||||
" (def captured @{:key \"deep\" :nested @[10 20 30]})\n"
|
||||
" (fn []\n"
|
||||
" (var result nil)\n"
|
||||
" (for i 0 500\n"
|
||||
" (def t @{:i i :s (string \"iter-\" i) :arr @[i (+ i 1) (+ i 2)]})\n"
|
||||
" (set result (get captured :key)))\n"
|
||||
" result)))\n"
|
||||
"\n"
|
||||
"(def outer-cb\n"
|
||||
" (do\n"
|
||||
" (def state @{:count 0 :data @[\"a\" \"b\" \"c\" \"d\" \"e\"]})\n"
|
||||
" (fn []\n"
|
||||
" # This runs on F2. Calling call-via-pcall here creates F3.\n"
|
||||
" # F2 becomes unreachable: it's not root_fiber (that's F1)\n"
|
||||
" # and it's no longer janet_vm.fiber (that's now F3).\n"
|
||||
" (def inner-result (call-via-pcall inner-cb))\n"
|
||||
" # If F2 was collected during F3's execution, accessing\n"
|
||||
" # state here reads freed memory.\n"
|
||||
" (put state :count (+ (state :count) 1))\n"
|
||||
" (string inner-result \"-\" (state :count)))))\n"
|
||||
"\n"
|
||||
"(for round 0 200\n"
|
||||
" (def result (call-via-pcall outer-cb))\n"
|
||||
" (def expected (string \"deep-\" (+ round 1)))\n"
|
||||
" (assert (= result expected)\n"
|
||||
" (string \"round \" round \": expected '\" expected \"', got '\" (describe result) \"'\")))\n";
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
int failures = 0;
|
||||
|
||||
janet_init();
|
||||
|
||||
JanetTable *env = janet_core_env(NULL);
|
||||
|
||||
janet_def(env, "call-via-pcall",
|
||||
janet_wrap_cfunction(cfun_call_via_pcall),
|
||||
"Call a function via janet_pcall from C.");
|
||||
|
||||
printf("Testing janet_pcall GC safety:\n");
|
||||
failures += run_test(env, "single-nesting", test_single);
|
||||
failures += run_test(env, "deep-nesting", test_deep);
|
||||
|
||||
janet_deinit();
|
||||
|
||||
if (failures > 0) {
|
||||
printf("\n%d test(s) FAILED\n", failures);
|
||||
return 1;
|
||||
}
|
||||
printf("\nAll tests passed.\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
# Helper code for running tests
|
||||
|
||||
# Turn on strict linting by default in test suite.
|
||||
(put root-env *lint-warn* :strict)
|
||||
|
||||
(var num-tests-passed 0)
|
||||
(var num-tests-run 0)
|
||||
(var suite-name 0)
|
||||
@@ -10,7 +7,7 @@
|
||||
(var skip-count 0)
|
||||
(var skip-n 0)
|
||||
|
||||
(var is-verbose (os/getenv "VERBOSE"))
|
||||
(def is-verbose (os/getenv "VERBOSE"))
|
||||
|
||||
(defn- assert-no-tail
|
||||
"Override's the default assert with some nice error handling."
|
||||
@@ -22,16 +19,15 @@
|
||||
(break x))
|
||||
(default e "assert error")
|
||||
(when x (++ num-tests-passed))
|
||||
(def str (string e))
|
||||
(def stack (debug/stack (fiber/current)))
|
||||
(def frame (last stack))
|
||||
(def line-info (string/format "%s:%d"
|
||||
(frame :source) (frame :source-line)))
|
||||
(if x
|
||||
(when is-verbose
|
||||
(eprintf "\e[32m✔\e[0m %s: %s: %v" line-info (describe e) x)
|
||||
(eflush) (flush))
|
||||
(when is-verbose (eprintf "\e[32m✔\e[0m %s: %s: %v" line-info (describe e) x))
|
||||
(do
|
||||
(eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x) (eflush) (flush)))
|
||||
(eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x) (eflush)))
|
||||
x)
|
||||
|
||||
(defn skip-asserts
|
||||
@@ -40,10 +36,10 @@
|
||||
(+= skip-n n)
|
||||
nil)
|
||||
|
||||
(defmacro assert :shadow
|
||||
(defmacro assert
|
||||
[x &opt e]
|
||||
(def xx (gensym))
|
||||
(default e (string/format "%j" x))
|
||||
(default e ~',x)
|
||||
~(do
|
||||
(def ,xx ,x)
|
||||
(,assert-no-tail ,xx ,e)
|
||||
@@ -52,12 +48,7 @@
|
||||
(defmacro assert-error
|
||||
[msg & forms]
|
||||
(def errsym (keyword (gensym)))
|
||||
~(as-macro ,assert (= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg))
|
||||
|
||||
(defmacro assert-error-value
|
||||
[msg errval & forms]
|
||||
(def e (gensym))
|
||||
~(as-macro ,assert (= ,errval (try (do ,;forms) ([,e] ,e))) ,msg))
|
||||
~(assert (= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg))
|
||||
|
||||
(defn check-compile-error
|
||||
[form]
|
||||
@@ -69,8 +60,8 @@
|
||||
(def e (gensym))
|
||||
(def f (gensym))
|
||||
(if is-verbose
|
||||
~(try (do ,;forms (as-macro ,assert true ,msg)) ([,e ,f] (as-macro ,assert false ,msg) (,debug/stacktrace ,f ,e "\e[31m✘\e[0m ")))
|
||||
~(try (do ,;forms (as-macro ,assert true ,msg)) ([_] (as-macro ,assert false ,msg)))))
|
||||
~(try (do ,;forms (,assert true ,msg)) ([,e ,f] (,assert false ,msg) (,debug/stacktrace ,f ,e "\e[31m✘\e[0m ")))
|
||||
~(try (do ,;forms (,assert true ,msg)) ([_] (,assert false ,msg)))))
|
||||
|
||||
(defn start-suite [&opt x]
|
||||
(default x (dyn :current-file))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2026 Calvin Rose
|
||||
# Copyright (c) 2023 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
|
||||
@@ -70,9 +70,9 @@
|
||||
(assert (= (array/pop @[]) nil) "array/pop empty")
|
||||
|
||||
# Code coverage
|
||||
(def a1 @[1])
|
||||
(array/pop a1)
|
||||
(array/trim a1)
|
||||
(def a @[1])
|
||||
(array/pop a)
|
||||
(array/trim a)
|
||||
(array/ensure @[1 1] 6 2)
|
||||
|
||||
# array/join
|
||||
@@ -86,10 +86,5 @@
|
||||
(assert-error "array/join error 4" (array/join @[] "abc123"))
|
||||
(assert-error "array/join error 5" (array/join @[] "abc123"))
|
||||
|
||||
# Regression 1714
|
||||
(repeat 10
|
||||
(assert (deep= (put @[] 100 10) (put (seq [_ :range [0 101]] nil) 100 10)) "regression 1714")
|
||||
(assert (deep= (put @[] 200 10) (put (seq [_ :range [0 201]] nil) 200 10)) "regression 1714"))
|
||||
|
||||
(end-suite)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2026 Calvin Rose
|
||||
# Copyright (c) 2023 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
|
||||
@@ -21,8 +21,6 @@
|
||||
(import ./helper :prefix "" :exit true)
|
||||
(start-suite)
|
||||
|
||||
(setdyn *lint-warn* :none)
|
||||
|
||||
# Assembly test
|
||||
# Fibonacci sequence, implemented with naive recursion.
|
||||
# a679f60
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2026 Calvin Rose
|
||||
# Copyright (c) 2023 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
|
||||
@@ -21,8 +21,6 @@
|
||||
(import ./helper :prefix "" :exit true)
|
||||
(start-suite)
|
||||
|
||||
(setdyn *lint-warn* :none)
|
||||
|
||||
# Let
|
||||
# 807f981
|
||||
(assert (= (let [a 1 b 2] (+ a b)) 3) "simple let")
|
||||
@@ -867,13 +865,6 @@
|
||||
(assert (deep= ~(,import* "a" :as "b" :fresh maybe)
|
||||
(macex '(import a :as b :fresh maybe))) "import macro 2")
|
||||
|
||||
# 2af3f21d
|
||||
(assert-error "import macro 2" (macex '(import a :fresh)))
|
||||
(assert-error "import macro 3" (macex '(import a :as b :fresh)))
|
||||
(assert-error "import macro 4" (macex '(import b "notakeyword" value)))
|
||||
(assert (deep= ~(,import* "a" :fresh nil)
|
||||
(macex '(import a :fresh nil))) "import macro 5")
|
||||
|
||||
# #477 walk preserving bracket type
|
||||
# 0a1d902f4
|
||||
(assert (= :brackets (tuple/type (postwalk identity '[])))
|
||||
@@ -905,18 +896,11 @@
|
||||
(struct/with-proto {:a [1 2 3]} :c 22 :b [1 2 3 4] :d "test" :e "test2"))
|
||||
(table/setproto table-to-freeze @{:a @[1 2 3]})
|
||||
|
||||
(assert (deep= struct-to-thaw (freeze table-to-freeze)))
|
||||
(assert (deep= {:a [1 2 3] :b [1 2 3 4] :c 22 :d "test" :e "test2"}
|
||||
(freeze table-to-freeze)))
|
||||
(assert (deep= table-to-freeze-with-inline-proto (thaw table-to-freeze)))
|
||||
(assert (deep= table-to-freeze-with-inline-proto (thaw struct-to-thaw)))
|
||||
|
||||
# Check that freezing mutable keys is deterministic
|
||||
# for issue #1535
|
||||
(def hashes @{})
|
||||
(repeat 200
|
||||
(def x (freeze {@"" 1 @"" 2 @"" 3 @"" 4 @"" 5}))
|
||||
(put hashes (hash x) true))
|
||||
(assert (= 1 (length hashes)) "freeze mutable keys is deterministic")
|
||||
|
||||
# Make sure Carriage Returns don't end up in doc strings
|
||||
# e528b86
|
||||
(assert (not (string/find "\r"
|
||||
@@ -1017,19 +1001,8 @@
|
||||
@{:key1 "value1" @"key" "value2"}) "deep= mutable keys"))
|
||||
(assert (deep-not= {"abc" 123} {@"abc" 123}) "deep= mutable keys vs immutable key")
|
||||
(assert (deep-not= {@"" 1 @"" 2 @"" 3} {@"" 1 @"" 2 @"" 3}) "deep= duplicate mutable keys")
|
||||
(assert (deep-not= {@"" @"" @"" @"" @"" 3} {@"" @"" @"" @"" @"" 3}) "deep= duplicate mutable keys 2")
|
||||
(assert (deep-not= {@[] @"" @[] @"" @[] 3} {@[] @"" @[] @"" @[] 3}) "deep= duplicate mutable keys 3")
|
||||
(assert (deep-not= {@{} @"" @{} @"" @{} 3} {@{} @"" @{} @"" @{} 3}) "deep= duplicate mutable keys 4")
|
||||
(assert (deep-not= @{:key1 "value1" @"key2" @"value2"}
|
||||
@{:key1 "value1" @"key2" "value2"}) "deep= mutable keys")
|
||||
(assert (deep-not= @{:key1 "value1" [@"key2"] @"value2"}
|
||||
@{:key1 "value1" [@"key2"] @"value2"}) "deep= mutable keys")
|
||||
|
||||
# different try overloads
|
||||
(assert (= (try (error :error) ([] :caught)) :caught))
|
||||
(assert (= (try (error :error) ([e] e)) :error))
|
||||
(assert (= (try (error :error) ([e fib] [e (fiber? fib)])) [:error true]))
|
||||
# regression test for #1659
|
||||
(assert (= (try (error :error) ([_ _] :caught)) :caught))
|
||||
(def k1 @"")
|
||||
(def k2 @"")
|
||||
(assert (deep= {k1 1 k2 2} {k1 1 k2 2}) "deep= duplicate mutable keys 2")
|
||||
|
||||
(end-suite)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2026 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
|
||||
@@ -48,8 +48,8 @@
|
||||
|
||||
(assert (deep= (buffer/push @"AA" @"BB") @"AABB") "buffer/push buffer")
|
||||
(assert (deep= (buffer/push @"AA" 66 66) @"AABB") "buffer/push int")
|
||||
(def b1 @"AA")
|
||||
(assert (deep= (buffer/push b1 b1) @"AAAA") "buffer/push buffer self")
|
||||
(def b @"AA")
|
||||
(assert (deep= (buffer/push b b) @"AAAA") "buffer/push buffer self")
|
||||
|
||||
# buffer/push-byte
|
||||
(assert (deep= (buffer/push-byte @"AA" 66) @"AAB") "buffer/push-byte")
|
||||
@@ -145,8 +145,8 @@
|
||||
|
||||
# Regression #301
|
||||
# a3d4ecddb
|
||||
(def b8 (buffer/new-filled 128 0x78))
|
||||
(assert (= 38 (length (buffer/blit @"" b8 -1 90))) "buffer/blit 1")
|
||||
(def b (buffer/new-filled 128 0x78))
|
||||
(assert (= 38 (length (buffer/blit @"" b -1 90))) "buffer/blit 1")
|
||||
|
||||
(def a @"abcdefghijklm")
|
||||
(assert (deep= @"abcde" (buffer/blit @"" a -1 0 5)) "buffer/blit 2")
|
||||
@@ -179,10 +179,5 @@
|
||||
(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"))
|
||||
|
||||
# Regression 1714
|
||||
(repeat 10
|
||||
(assert (deep= (put @"" 100 10) (put (buffer (string/repeat "\0" 101)) 100 10)) "regression 1714")
|
||||
(assert (deep= (put @"" 200 10) (put (buffer (string/repeat "\0" 201)) 200 10)) "regression 1714"))
|
||||
|
||||
(end-suite)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2026 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
|
||||
@@ -117,17 +117,8 @@
|
||||
(assert (= 0 (length (bundle/list))) "bundles are listed correctly 7")
|
||||
(assert (= 0 (length (bundle/topolist))) "bundles are listed correctly 8")
|
||||
|
||||
# Try installing a bundle that is missing bundle script
|
||||
(assert-error-value "bundle missing bundle script"
|
||||
"bundle must contain bundle.janet or bundle/init.janet"
|
||||
(bundle/install "./examples/sample-bad-bundle1"))
|
||||
(assert (= 0 (length (bundle/list))) "check failure 0")
|
||||
(assert (= 0 (length (bundle/topolist))) "check failure 1")
|
||||
|
||||
# Try installing a bundle that fails check
|
||||
(assert-error-value "bundle check hook fails"
|
||||
"Check failed!"
|
||||
(bundle/install "./examples/sample-bad-bundle2" :check true))
|
||||
(assert-error "bad test" (bundle/install "./examples/sample-bad-bundle" :check true))
|
||||
(assert (= 0 (length (bundle/list))) "check failure 0")
|
||||
(assert (= 0 (length (bundle/topolist))) "check failure 1")
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2026 Calvin Rose
|
||||
# Copyright (c) 2023 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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user