From e1963cdfe0148f83601907727e00bd672d798115 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Thu, 10 Sep 2026 16:45:02 -0500 Subject: [PATCH 01/12] Prepare for version 1.42.1 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d47c0e12..a17ed803 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog All notable changes to this project will be documented in this file. -## 1.42.1 - ??? +## 1.42.1 - 2026-09-10 +- More changes to documentation. - Correct SONAME in Makefile ## 1.42.0 - 2026-08-31 From 0bfcc9fc97bc1db9a40d447d67f42d5e12156bda Mon Sep 17 00:00:00 2001 From: Pietro Cerutti Date: Fri, 11 Sep 2026 00:12:54 +0200 Subject: [PATCH 02/12] Fix a couple of imprecisions in the file/ docs (#1848) * Fix a couple of imprecisions in the file/ docs The `+` flag to `(file/open)` was incorrectly documented as being for appending. Instead, this allows to read to files opened with `:w` or `:a`, and to write to files opened with `:r`. This is the same as the `+` flag in `fopen(3)`. The `(file/seek)` function was documented as having both `whence` and `n` as optional arguments. It also didn't say what the default for `n` was. Anyway, this doesn't make sense, as a default of `:cur` and `0` would effectively make `(file/seek f)` a no-op. I also amended a test so both `(file/seek f :set 0)` and `(file/seek f :set)` are covered and checked to move to the beginning of the file. * Remove useless check for argc >= 2 We already determined it is between 2 and 3 with `janet_arity(argc, 2, 3)`. * Dump args in assertion * Unroll loop in test * Differentiate failure messages --- src/core/io.c | 32 +++++++++++++++----------------- test/suite-io.janet | 5 ++++- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/core/io.c b/src/core/io.c index 61488cb8..3bbda25b 100644 --- a/src/core/io.c +++ b/src/core/io.c @@ -150,7 +150,7 @@ JANET_CORE_FN(cfun_io_fopen, "* a - append to the file\n\n" "Following one of the initial flags, 0 or more of the following flags can be appended:\n\n" "* b - open the file in binary mode (rather than text mode)\n\n" - "* + - append to the file instead of overwriting it\n\n" + "* + - open the file for both reading and writing\n\n" "* n - error if the file cannot be opened instead of returning nil\n\n" "See fopen (, C99) for further details.") { janet_arity(argc, 1, 3); @@ -342,12 +342,12 @@ JANET_CORE_FN(cfun_io_fclose, /* Seek a file */ JANET_CORE_FN(cfun_io_fseek, - "(file/seek f &opt whence n)", + "(file/seek f whence &opt n)", "Jump to a relative location in the file `f`. `whence` must be one of:\n\n" "* :cur - jump relative to the current file location\n\n" "* :set - jump relative to the beginning of the file\n\n" "* :end - jump relative to the end of the file\n\n" - "By default, `whence` is :cur. Optionally a value `n` may be passed " + "By default, `n` is 0. Optionally a value `n` may be passed " "for the relative number of bytes to seek in the file. `n` may be a real " "number to handle large files of more than 4GB. Returns the file handle.") { janet_arity(argc, 2, 3); @@ -356,20 +356,18 @@ JANET_CORE_FN(cfun_io_fseek, janet_panic("file is closed"); int64_t offset = 0; int whence = SEEK_CUR; - if (argc >= 2) { - const uint8_t *whence_sym = janet_getkeyword(argv, 1); - if (!janet_cstrcmp(whence_sym, "cur")) { - whence = SEEK_CUR; - } else if (!janet_cstrcmp(whence_sym, "set")) { - whence = SEEK_SET; - } else if (!janet_cstrcmp(whence_sym, "end")) { - whence = SEEK_END; - } else { - janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]); - } - if (argc == 3) { - offset = (int64_t) janet_getinteger64(argv, 2); - } + const uint8_t *whence_sym = janet_getkeyword(argv, 1); + if (!janet_cstrcmp(whence_sym, "cur")) { + whence = SEEK_CUR; + } else if (!janet_cstrcmp(whence_sym, "set")) { + whence = SEEK_SET; + } else if (!janet_cstrcmp(whence_sym, "end")) { + whence = SEEK_END; + } else { + janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]); + } + if (argc == 3) { + offset = (int64_t) janet_getinteger64(argv, 2); } if (fseek(iof->file, offset, whence)) janet_panic("error seeking file"); return argv[0]; diff --git a/test/suite-io.janet b/test/suite-io.janet index 23672f62..6062b518 100644 --- a/test/suite-io.janet +++ b/test/suite-io.janet @@ -55,7 +55,10 @@ (file/flush f) (file/seek f :set 0) (assert (= 0 (file/tell f)) "start of file again") - (assert (= (string (file/read f :all)) "foo\n") "temp files work")) + (assert (= (string (file/read f :all)) "foo\n") "temp files work") + (file/seek f :set) + (assert (= 0 (file/tell f)) "start of file again 2") + (assert (= (string (file/read f :all)) "foo\n") "temp files work 2")) # issue #1055 - 2c927ea76 (let [b @""] From cae73db4afbad805517b4004430cbf2199237b1c Mon Sep 17 00:00:00 2001 From: sogaiu <33044872+sogaiu@users.noreply.github.com> Date: Thu, 10 Sep 2026 22:13:30 +0000 Subject: [PATCH 03/12] Fix misc docstring typos (#1855) Co-authored-by: sogaiu <983021772@users.noreply.github.com> --- src/boot/boot.janet | 16 +++++++++------- src/core/array.c | 2 +- src/core/buffer.c | 10 +++++----- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 7b080632..cd52d880 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -49,10 +49,12 @@ (apply defn name :macro more)) (defmacro as-macro - ``Use a function or macro literal `f` as a macro. This lets - any function be used as a macro. Inside a quasiquote, the - idiom `(as-macro ,my-custom-macro arg1 arg2...)` can be used - to avoid unwanted variable capture of `my-custom-macro`.`` + `` + Use a function or macro literal `f` as a macro. This allows any + function to be used as a macro. Inside a quasiquote, the idiom + `(as-macro ,my-custom-macro arg1 arg2...)` can be used to avoid + unwanted variable capture of `my-custom-macro`. + `` [f & args] (f ;args)) @@ -177,7 +179,7 @@ "The current lint error level. The error level is the lint level at which compilation will exit with an error and not continue.") (defdyn *lint-warn* - "The current lint warning level. The warning level is the lint level at which and error will be printed but compilation will continue as normal.") + "The current lint warning level. The warning level is the lint level at which an error will be printed but compilation will continue as normal.") (defdyn *lint-levels* "A table of keyword alias to numbers denoting a lint level. Can be used to provided custom aliases for numeric lint levels.") @@ -4180,7 +4182,7 @@ (defmacro ev/spawn `` Run some code in a new task fiber. This is shorthand for - `(ev/go (fn [] ;body))`." + `(ev/go (fn [] ;body))`. `` [& body] ~(,ev/go (fn :spawn [&] ,;body))) @@ -4322,7 +4324,7 @@ (compwhen (dyn 'ffi/native) - (defdyn *ffi-context* " Current native library for ffi/bind and other settings") + (defdyn *ffi-context* " Current native library for ffi/defbind and other settings") (defn- default-mangle [name &] diff --git a/src/core/array.c b/src/core/array.c index c8ebcc58..8076c032 100644 --- a/src/core/array.c +++ b/src/core/array.c @@ -385,7 +385,7 @@ JANET_CORE_FN(cfun_array_trim, JANET_CORE_FN(cfun_array_clear, "(array/clear arr)", - "Empties an array, setting it's count to 0 but does not free the backing capacity. " + "Empties an array, setting its count to 0 but does not free the backing capacity. " "Returns the modified array.") { janet_fixarity(argc, 1); JanetArray *array = janet_getarray(argv, 0); diff --git a/src/core/buffer.c b/src/core/buffer.c index 5a1e08b2..83389e9b 100644 --- a/src/core/buffer.c +++ b/src/core/buffer.c @@ -368,7 +368,7 @@ static void reverse_u64(uint8_t bytes[8]) { JANET_CORE_FN(cfun_buffer_push_uint16, "(buffer/push-uint16 buffer order data)", "Push a 16 bit unsigned integer data onto the end of the buffer. " - "Returns the modified buffer." + "Returns the modified buffer. " "Expands the buffer as necessary. Throws an error if size limit is exceeded.") { janet_fixarity(argc, 3); JanetBuffer *buffer = janet_getbuffer(argv, 0); @@ -388,7 +388,7 @@ JANET_CORE_FN(cfun_buffer_push_uint16, JANET_CORE_FN(cfun_buffer_push_uint32, "(buffer/push-uint32 buffer order data)", "Push a 32 bit unsigned integer data onto the end of the buffer. " - "Returns the modified buffer." + "Returns the modified buffer. " "Expands the buffer as necessary. Throws an error if size limit is exceeded.") { janet_fixarity(argc, 3); JanetBuffer *buffer = janet_getbuffer(argv, 0); @@ -405,7 +405,7 @@ JANET_CORE_FN(cfun_buffer_push_uint32, JANET_CORE_FN(cfun_buffer_push_uint64, "(buffer/push-uint64 buffer order data)", "Push a 64 bit unsigned integer data onto the end of the buffer. " - "Returns the modified buffer." + "Returns the modified buffer. " "Expands the buffer as necessary. Throws an error if size limit is exceeded.") { janet_fixarity(argc, 3); JanetBuffer *buffer = janet_getbuffer(argv, 0); @@ -422,7 +422,7 @@ JANET_CORE_FN(cfun_buffer_push_uint64, JANET_CORE_FN(cfun_buffer_push_float32, "(buffer/push-float32 buffer order data)", "Push the underlying bytes of a 32 bit float data onto the end of the buffer. " - "Returns the modified buffer." + "Returns the modified buffer. " "Expands the buffer as necessary. Throws an error if size limit is exceeded.") { janet_fixarity(argc, 3); JanetBuffer *buffer = janet_getbuffer(argv, 0); @@ -439,7 +439,7 @@ JANET_CORE_FN(cfun_buffer_push_float32, JANET_CORE_FN(cfun_buffer_push_float64, "(buffer/push-float64 buffer order data)", "Push the underlying bytes of a 64 bit float data onto the end of the buffer. " - "Returns the modified buffer." + "Returns the modified buffer. " "Expands the buffer as necessary. Throws an error if size limit is exceeded.") { janet_fixarity(argc, 3); JanetBuffer *buffer = janet_getbuffer(argv, 0); From 112f5aef7da5f1f8a3fea3ab100d62bf4766cfef Mon Sep 17 00:00:00 2001 From: sogaiu <33044872+sogaiu@users.noreply.github.com> Date: Thu, 10 Sep 2026 22:13:47 +0000 Subject: [PATCH 04/12] Use backticks for nicer site rendering (#1856) Co-authored-by: sogaiu <983021772@users.noreply.github.com> --- src/core/ffi.c | 15 ++++++++------ src/core/os.c | 55 ++++++++++++++++++++++++++++---------------------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/core/ffi.c b/src/core/ffi.c index d638e896..6801023f 100644 --- a/src/core/ffi.c +++ b/src/core/ffi.c @@ -1739,12 +1739,15 @@ JANET_CORE_FN(cfun_ffi_buffer_read, JANET_CORE_FN(cfun_ffi_get_callback_trampoline, "(ffi/trampoline cc)", - "Get a native function pointer that can be used as a callback and passed to C libraries. " - "This callback trampoline has the signature `void trampoline(void \\*ctx, void \\*userdata)` in " - "the given calling convention. This is the only function signature supported. " - "It is up to the programmer to ensure that the `userdata` argument contains a janet function " - "the will be called with one argument, `ctx` which is an opaque pointer. This pointer can " - "be further inspected with `ffi/read`.") { + "Get a native function pointer that can be used as a callback " + "and passed to C libraries. This callback trampoline has the " + "signature `void trampoline(void *ctx, void *userdata)` in " + "the given calling convention. This is the only function " + "signature supported. It is up to the programmer to ensure " + "that the `userdata` argument contains a janet function that " + "will be called with one argument, `ctx` which is an opaque " + "pointer. This pointer can be further inspected with " + "`ffi/read`.") { janet_arity(argc, 0, 1); JanetFFICallingConvention cc = JANET_FFI_CC_DEFAULT; if (argc >= 1) cc = decode_ffi_cc(janet_getkeyword(argv, 0)); diff --git a/src/core/os.c b/src/core/os.c index c46f05e5..66fe3c6e 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -2715,32 +2715,39 @@ static jmode_t os_optmode(int32_t argc, const Janet *argv, int32_t n, int32_t df JANET_CORE_FN(os_open, "(os/open path &opt flags mode)", - "Create a stream from a file, like the POSIX open system call. Returns a new stream. " - "`mode` should be a file mode as passed to `os/chmod`, but only if the create flag is given. " - "The default mode is 8r666. " - "Allowed flags are as follows:\n\n" + "Create a stream from a file, like the POSIX open system " + "call. Returns a new stream. `mode` should be a file mode " + "as passed to `os/chmod`, but only if the create flag is " + "given. The default mode is 8r666.\n" + "\n" + "Allowed flags are as follows:\n" + "\n" " * :r - open this file for reading\n" " * :w - open this file for writing\n" - " * :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" - " * :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" - " * :D - share deletes (FILE\\_SHARE\\_DELETE)\n" - " * :H - FILE\\_ATTRIBUTE\\_HIDDEN\n" - " * :O - FILE\\_ATTRIBUTE\\_READONLY\n" - " * :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") { + " * :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" + " * :a - append to a file (`O_APPEND` on posix, `FILE_APPEND_DATA` on windows)\n" + "\n" + "Posix-only flags:\n" + "\n" + " * :x - `O_SYNC`\n" + " * :C - `O_NOCTTY`\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" + " * :D - share deletes (`FILE_SHARE_DELETE`)\n" + " * :H - `FILE_ATTRIBUTE_HIDDEN`\n" + " * :O - `FILE_ATTRIBUTE_READONLY`\n" + " * :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"); From 9bc56342a8aa34ce7ba182255c88ed2bc6886d0c Mon Sep 17 00:00:00 2001 From: sogaiu <33044872+sogaiu@users.noreply.github.com> Date: Thu, 10 Sep 2026 22:14:08 +0000 Subject: [PATCH 05/12] Improve docstring for zipcoll (#1854) Co-authored-by: sogaiu <983021772@users.noreply.github.com> --- src/boot/boot.janet | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index cd52d880..cae3430e 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -1790,8 +1790,14 @@ ret) (defn zipcoll - `Creates a table from two arrays/tuples. - Returns a new table.` + `` + Creates a table from `ks` and `vs` by pairing values at the same + index from each. If `ks` or `vs` has more values than the other, the + extra values are ignored. Returns a new table. + + `ks` and `vs` can by bytes, indexed, fibers, or abstract types with + suitable `get` and `next` methods. + `` [ks vs] (def res @{}) (var kk nil) From e6194a81880dfdda5d3054914b886b0932c0fdd3 Mon Sep 17 00:00:00 2001 From: sogaiu <33044872+sogaiu@users.noreply.github.com> Date: Fri, 11 Sep 2026 00:32:48 +0000 Subject: [PATCH 06/12] Improve docstring for doc (#1852) Co-authored-by: sogaiu <983021772@users.noreply.github.com> --- src/boot/boot.janet | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index cae3430e..785f564d 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -3928,12 +3928,15 @@ (print-index identity))) (defmacro doc - ``Shows documentation for the given symbol, or can show a list of available bindings. - If `sym` is a symbol, will look for documentation for that symbol. If `sym` is a string - or is not provided, will show all lexical and dynamic bindings in the current environment - containing that string (all bindings will be shown if no string is given).`` - [&opt sym] - ~(,doc* ',sym)) + `` + Shows documentation for `what` or lists binding names. If `what` is + a symbol, shows documentation for that symbol. If `what` is a + string, shows all lexical and dynamic binding names in the current + environment containing the string. If `what` is not provided, shows + all binding names. + `` + [&opt what] + ~(,doc* ',what)) (defn doc-of `Searches all loaded modules in module/cache for a given binding and prints out its documentation. From 6bbdfd0e0cc20412713a2fbb4185a4458f50a97a Mon Sep 17 00:00:00 2001 From: sogaiu <33044872+sogaiu@users.noreply.github.com> Date: Sat, 12 Sep 2026 01:50:08 +0000 Subject: [PATCH 07/12] Surround more things with backticks (#1862) Co-authored-by: sogaiu <983021772@users.noreply.github.com> --- src/boot/boot.janet | 2 +- src/core/corelib.c | 2 +- src/core/filewatch.c | 170 ++++++++++++++++++++++++++----------------- src/core/io.c | 29 +++++--- src/core/os.c | 46 +++++++----- 5 files changed, 148 insertions(+), 101 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 785f564d..23e87dbe 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -4333,7 +4333,7 @@ (compwhen (dyn 'ffi/native) - (defdyn *ffi-context* " Current native library for ffi/defbind and other settings") + (defdyn *ffi-context* "Current native library for `ffi/defbind` and other settings") (defn- default-mangle [name &] diff --git a/src/core/corelib.c b/src/core/corelib.c index 541587bc..a375b8ae 100644 --- a/src/core/corelib.c +++ b/src/core/corelib.c @@ -1358,7 +1358,7 @@ JanetTable *janet_core_env(JanetTable *replacements) { janet_def(env, "janet/build", janet_cstringv(JANET_BUILD), 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 " + JDOC("The flag set of config options from `janetconf.h` which is used to check " "if native modules are compatible with the host program.")); /* Allow references to the environment */ diff --git a/src/core/filewatch.c b/src/core/filewatch.c index aee6b17e..f652ab47 100644 --- a/src/core/filewatch.c +++ b/src/core/filewatch.c @@ -835,20 +835,38 @@ static const JanetAbstractType janet_filewatch_at = { }; JANET_CORE_FN(cfun_filewatch_make, - "(filewatch/new channel & 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" - "* `:type` -- the type of the event that was raised.\n\n" - "* `:file-name` -- the base file name of the file that triggered the event.\n\n" - "* `:dir-name` -- the directory name of the file that triggered the event.\n\n" - "Events also will contain keys specific to the host OS.\n\n" - "Windows has no extra properties on events.\n\n" - "Linux and the BSDs have the following extra properties on events:\n\n" - "* `:wd` -- the integer key returned by `filewatch/add` for the path that triggered this. This is a file descriptor integer on BSD and macos.\n\n" - "* `:wd-path` -- the string path for watched directory of file. For files, will be the same as `:file-name`, and for directories, will be the same as `:dir-name`.\n\n" - "* `:cookie` -- a semi-randomized integer used to associate related events, such as :moved-from and :moved-to events.\n\n" - "") { + "(filewatch/new chan & default-flags)", + "Create a new filewatcher that will give events to a channel " + "`chan`. See `filewatch/add` for available flags.\n" + "\n" + "When an event is triggered by the filewatcher, a struct " + "containing information will be given to `chan` as with " + "`ev/give`. The contents of `chan` depend on the OS, but will " + "contain some common keys:\n" + "\n" + "* `:type` - type of the event that was raised\n" + "* `:file-name` - base file name of file that triggered the " + "event\n" + "* `:dir-name` - directory name of file that triggered the " + "event\n" + "\n" + "Events will also contain keys specific to the host OS.\n" + "\n" + "Windows has no extra properties on events.\n" + "\n" + "Linux and BSDs have the following extra properties on " + "events:\n" + "\n" + "* `:wd` - integer key returned by `filewatch/add` for path " + "that triggered this. This is a file descriptor integer on " + "BSD and macos.\n" + "\n" + "* `:wd-path` - string path for watched directory of file. " + "For files, same as `:file-name`, and for directories, same " + "as `:dir-name`.\n" + "\n" + "* `:cookie` - a semi-randomized integer used to associate " + "related events, such as `:moved-from` and `:moved-to`.") { janet_sandbox_assert(JANET_SANDBOX_FS_READ); janet_arity(argc, 1, -1); JanetChannel *channel = janet_getchannel(argv, 0); @@ -859,60 +877,76 @@ JANET_CORE_FN(cfun_filewatch_make, } JANET_CORE_FN(cfun_filewatch_add, - "(filewatch/add watcher path flag & more-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" - "FLAGS\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" - "* `:recursive` - watch subdirectories recursively\n\n" - "Linux (flags correspond to `IN_*` flags from ):\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" - "BSDs and macos (flags correspond to `NOTE_*` flags from ). Not all flags are available on all systems:\n\n" - "* `:all` - `All available NOTE_* flags on the current platform`\n\n" - "* `:attrib` - `NOTE_ATTRIB`\n\n" - "* `:close-write` - `NOTE_CLOSE_WRITE`\n\n" - "* `:close` - `NOTE_CLOSE`\n\n" - "* `:delete` - `NOTE_DELETE`\n\n" - "* `:extend` - `NOTE_EXTEND`\n\n" - "* `:funlock` - `NOTE_FUNLOCK`\n\n" - "* `:link` - `NOTE_LINK`\n\n" - "* `:open` - `NOTE_OPEN`\n\n" - "* `:read` - `NOTE_READ`\n\n" - "* `:rename` - `NOTE_RENAME`\n\n" - "* `:revoke` - `NOTE_REVOKE`\n\n" - "* `:truncate` - `NOTE_TRUNCATE`\n\n" - "* `:write` - `NOTE_WRITE`\n\n\n" - "EVENT TYPES\n\n" - "On Windows, events will have the following possible types:\n\n" - "* `:unknown`\n\n" - "* `:added`\n\n" - "* `:removed`\n\n" - "* `:modified`\n\n" - "* `:renamed-old`\n\n" - "* `:renamed-new`\n\n" - "On Linux and BSDs, events will have a `:type` corresponding to the possible flags, excluding `:all`.\n" + "(filewatch/add watcher path flags & more-flags)", + "Add a path to the watcher. Available flags and event types " + "depend on the current OS, and are as follows.\n" + "\n" + "Flags\n" + "\n" + "Windows/MINGW (correspond to `FILE_NOTIFY_CHANGE_*` flags in " + "win32 documentation):\n" + "\n" + "* `:all` - trigger an event for all of the below triggers.\n" + "* `:attributes` - `FILE_NOTIFY_CHANGE_ATTRIBUTES`\n" + "* `:creation` - `FILE_NOTIFY_CHANGE_CREATION`\n" + "* `:dir-name` - `FILE_NOTIFY_CHANGE_DIR_NAME`\n" + "* `:last-access` - `FILE_NOTIFY_CHANGE_LAST_ACCESS`\n" + "* `:last-write` - `FILE_NOTIFY_CHANGE_LAST_WRITE`\n" + "* `:security` - `FILE_NOTIFY_CHANGE_SECURITY`\n" + "* `:size` - `FILE_NOTIFY_CHANGE_SIZE`\n" + "* `:recursive` - watch subdirectories recursively\n" + "\n" + "Linux (correspond to `IN_*` flags from ``):\n" + "\n" + "* `:access` - `IN_ACCESS`\n" + "* `:all` - `IN_ALL_EVENTS`\n" + "* `:attrib` - `IN_ATTRIB`\n" + "* `:close-nowrite` - `IN_CLOSE_NOWRITE`\n" + "* `:close-write` - `IN_CLOSE_WRITE`\n" + "* `:create` - `IN_CREATE`\n" + "* `:delete` - `IN_DELETE`\n" + "* `:delete-self` - `IN_DELETE_SELF`\n" + "* `:ignored` - `IN_IGNORED`\n" + "* `:modify` - `IN_MODIFY`\n" + "* `:move-self` - `IN_MOVE_SELF`\n" + "* `:moved-from` - `IN_MOVED_FROM`\n" + "* `:moved-to` - `IN_MOVED_TO`\n" + "* `:open` - `IN_OPEN`\n" + "* `:q-overflow` - `IN_Q_OVERFLOW`\n" + "* `:unmount` - `IN_UNMOUNT`\n" + "\n" + "BSDs and macos (correspond to `NOTE_*` flags from " + "``). Not all flags are available on all " + "systems:\n" + "\n" + "* `:all` - All available `NOTE_*` flags on the current platform\n" + "* `:attrib` - `NOTE_ATTRIB`\n" + "* `:close-write` - `NOTE_CLOSE_WRITE`\n" + "* `:close` - `NOTE_CLOSE`\n" + "* `:delete` - `NOTE_DELETE`\n" + "* `:extend` - `NOTE_EXTEND`\n" + "* `:funlock` - `NOTE_FUNLOCK`\n" + "* `:link` - `NOTE_LINK`\n" + "* `:open` - `NOTE_OPEN`\n" + "* `:read` - `NOTE_READ`\n" + "* `:rename` - `NOTE_RENAME`\n" + "* `:revoke` - `NOTE_REVOKE`\n" + "* `:truncate` - `NOTE_TRUNCATE`\n" + "* `:write` - `NOTE_WRITE`\n" + "\n" + "Event Types\n" + "\n" + "On Windows, events will have the following possible types:\n" + "\n" + "* `:unknown`\n" + "* `:added`\n" + "* `:removed`\n" + "* `:modified`\n" + "* `:renamed-old`\n" + "* `:renamed-new`\n" + "\n" + "On Linux and BSDs, events will have a `:type` corresponding " + "to the possible flags, excluding `:all`.\n" "") { janet_sandbox_assert(JANET_SANDBOX_FS_READ); janet_arity(argc, 2, -1); diff --git a/src/core/io.c b/src/core/io.c index 3bbda25b..b54849bb 100644 --- a/src/core/io.c +++ b/src/core/io.c @@ -141,18 +141,23 @@ JANET_CORE_FN(cfun_io_temp, JANET_CORE_FN(cfun_io_fopen, "(file/open path &opt mode buffer-size)", "Open a file. `path` is an absolute or relative path, and " - "`mode` is a set of flags indicating the mode to open the file in. " - "`mode` is a keyword where each character represents a flag. If the file " - "cannot be opened, returns nil, otherwise returns the new file handle. " - "Mode flags:\n\n" - "* r - allow reading from the file\n\n" - "* w - allow writing to the file\n\n" - "* a - append to the file\n\n" - "Following one of the initial flags, 0 or more of the following flags can be appended:\n\n" - "* b - open the file in binary mode (rather than text mode)\n\n" - "* + - open the file for both reading and writing\n\n" - "* n - error if the file cannot be opened instead of returning nil\n\n" - "See fopen (, C99) for further details.") { + "`mode` is a set of flags indicating the mode to open the " + "file in. `mode` is a keyword where each character represents " + "a flag. If the file cannot be opened, returns nil, otherwise " + "returns the new file handle. Mode flags:\n" + "\n" + "* `r` - allow reading from file\n" + "* `w` - allow writing to file\n" + "* `a` - append to file\n" + "\n" + "After one of the initial flags, 0 or more of the following " + "flags can be appended:\n" + "\n" + "* `b` - open file in binary mode (rather than text mode)\n" + "* `+` - open file for both reading and writing\n" + "* `n` - error if file cannot be opened instead of returning nil\n" + "\n" + "See `fopen()` (``, C99) for further details.") { janet_arity(argc, 1, 3); const uint8_t *fname = janet_getstring(argv, 0); const uint8_t *fmode; diff --git a/src/core/os.c b/src/core/os.c index 66fe3c6e..85a6ae30 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -1916,20 +1916,25 @@ 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. " - "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" - "* :seconds - number of seconds [0-61]\n\n" - "* :minutes - number of minutes [0-59]\n\n" - "* :hours - number of hours [0-23]\n\n" - "* :month-day - day of month [0-30]\n\n" - "* :month - month of year [0, 11]\n\n" - "* :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() or _tzset() for further details.") { + "Returns the given time as a date struct, or the current time " + "if `time` is not given. 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-value pairs. " + "Note that all numbers are 0-indexed.\n" + "\n" + "* `:seconds` - number of seconds [0-61]\n" + "* `:minutes` - number of minutes [0-59]\n" + "* `:hours` - number of hours [0-23]\n" + "* `:month-day` - day of month [0-30]\n" + "* `:month` - month of year [0, 11]\n" + "* `:year` - years since year 0 (e.g. 2019)\n" + "* `:week-day` - day of the week [0-6]\n" + "* `:year-day` - day of the year [0-365]\n" + "* `:dst` - if Day Light Savings is in effect\n" + "\n" + "The local timezone can be set by setting the `TZ` " + "environment variable. See `tzset()` (``) or " + "`_tzset()` (``) for further details.") { janet_arity(argc, 0, 2); (void) argv; struct tm t_infos; @@ -1951,11 +1956,14 @@ JANET_CORE_FN(os_date, 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() or _tzset() for further details.") { + "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. The local timezone " + "can be set by setting the `TZ` environment variable. See " + "`tzset()` (``) or `_tzset()`(``) for further " + "details.") { janet_arity(argc, 1, 3); const char *fmt = janet_getcstring(argv, 0); /* ANSI X3.159-1989, section 4.12.3.5 "The strftime function" */ From 5deff0f13c4e2f2d9e73997ead210ad647aa4668 Mon Sep 17 00:00:00 2001 From: sogaiu <33044872+sogaiu@users.noreply.github.com> Date: Sat, 12 Sep 2026 01:55:28 +0000 Subject: [PATCH 08/12] Modify array/ docstrings (#1866) Co-authored-by: sogaiu <983021772@users.noreply.github.com> --- src/core/array.c | 80 ++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/src/core/array.c b/src/core/array.c index 8076c032..789b7363 100644 --- a/src/core/array.c +++ b/src/core/array.c @@ -135,8 +135,9 @@ Janet janet_array_peek(JanetArray *array) { JANET_CORE_FN(cfun_array_new, "(array/new capacity)", - "Creates a new empty array with a pre-allocated capacity. The same as " - "`(array)` but can be more efficient if the maximum size of an array is known.") { + "Creates and returns a new empty array with a pre-allocated " + "capacity. The same as `array` but can be more efficient if " + "the maximum size is known.") { janet_fixarity(argc, 1); int32_t cap = janet_getinteger(argv, 0); JanetArray *array = janet_array(cap); @@ -145,7 +146,9 @@ JANET_CORE_FN(cfun_array_new, JANET_CORE_FN(cfun_array_weak, "(array/weak capacity)", - "Creates a new empty array with a pre-allocated capacity and support for weak references. Similar to `array/new`.") { + "Creates and returns a new empty array with a pre-allocated " + "capacity and support for weak references. Similar to " + "`array/new`.") { janet_fixarity(argc, 1); int32_t cap = janet_getinteger(argv, 0); JanetArray *array = janet_array_weak(cap); @@ -153,8 +156,9 @@ JANET_CORE_FN(cfun_array_weak, } JANET_CORE_FN(cfun_array_new_filled, - "(array/new-filled count &opt value)", - "Creates a new array of `count` elements, all set to `value`, which defaults to nil. Returns the new array.") { + "(array/new-filled n &opt val)", + "Creates and returns a new array of `n` values, all set to " + "`val`, which defaults to nil.") { janet_arity(argc, 1, 2); int32_t count = janet_getnat(argv, 0); Janet x = (argc == 2) ? argv[1] : janet_wrap_nil(); @@ -167,9 +171,10 @@ JANET_CORE_FN(cfun_array_new_filled, } JANET_CORE_FN(cfun_array_fill, - "(array/fill arr &opt value)", - "Replace all elements of an array with `value` (defaulting to nil) without changing the length of the array. " - "Returns the modified array.") { + "(array/fill arr &opt val)", + "Replace all values of an array `arr` with `val` " + "(defaulting to nil) without changing the length of `arr`. " + "Returns `arr`.") { janet_arity(argc, 1, 2); JanetArray *array = janet_getarray(argv, 0); Janet x = (argc == 2) ? argv[1] : janet_wrap_nil(); @@ -181,8 +186,8 @@ JANET_CORE_FN(cfun_array_fill, JANET_CORE_FN(cfun_array_pop, "(array/pop arr)", - "Remove the last element of the array and return it. If the array is empty, will return nil. Modifies " - "the input array.") { + "Remove and return the last value of an array `arr`. If " + "`arr` is empty, returns nil. Modifies `arr`.") { janet_fixarity(argc, 1); JanetArray *array = janet_getarray(argv, 0); return janet_array_pop(array); @@ -190,7 +195,8 @@ JANET_CORE_FN(cfun_array_pop, JANET_CORE_FN(cfun_array_peek, "(array/peek arr)", - "Returns the last element of the array. Does not modify the array.") { + "Returns the last value of an array `arr`. Does not modify " + "`arr`.") { janet_fixarity(argc, 1); JanetArray *array = janet_getarray(argv, 0); return janet_array_peek(array); @@ -198,7 +204,8 @@ JANET_CORE_FN(cfun_array_peek, JANET_CORE_FN(cfun_array_push, "(array/push arr & xs)", - "Push all the elements of xs to the end of an array. Modifies the input array and returns it.") { + "Push all the values of `xs` to the end of an array `arr`. " + "Returns `arr`.") { janet_arity(argc, 1, -1); JanetArray *array = janet_getarray(argv, 0); if (INT32_MAX - argc + 1 <= array->count) { @@ -213,10 +220,12 @@ JANET_CORE_FN(cfun_array_push, JANET_CORE_FN(cfun_array_ensure, "(array/ensure arr capacity growth)", - "Ensures that the memory backing the array is large enough for `capacity` " - "items at the given rate of growth. `capacity` and `growth` must be integers. " - "If the backing capacity is already enough, then this function does nothing. " - "Otherwise, the backing memory will be reallocated so that there is enough space.") { + "Ensures that the memory backing the array `arr` is large " + "enough for `capacity` items at the given rate `growth`. " + "`capacity` and `growth` must be integers. If the backing " + "capacity is already enough, then there is no change. " + "Otherwise, the backing memory will be reallocated so that " + "there is enough space. Returns `arr`.") { janet_fixarity(argc, 3); JanetArray *array = janet_getarray(argv, 0); int32_t newcount = janet_getinteger(argv, 1); @@ -244,10 +253,11 @@ JANET_CORE_FN(cfun_array_slice, JANET_CORE_FN(cfun_array_concat, "(array/concat arr & parts)", - "Concatenates a variable number of arrays (and tuples) into the first argument, " - "which must be an array. If any of the parts are arrays or tuples, their elements will " - "be inserted into the array. Otherwise, each part in `parts` will be appended to `arr` in order. " - "Return the modified array `arr`.") { + "Concatenates `parts`, a variable number of arrays and " + "tuples, into the first argument `arr` which must be an " + "array. If any of `parts` are arrays or tuples, their values " + "are inserted into `arr`. Otherwise, each value in `parts` " + "will be appended to `arr` in order. Returns `arr`.") { int32_t i; janet_arity(argc, 1, -1); JanetArray *array = janet_getarray(argv, 0); @@ -277,9 +287,9 @@ JANET_CORE_FN(cfun_array_concat, JANET_CORE_FN(cfun_array_join, "(array/join arr & parts)", - "Join a variable number of arrays and tuples into the first argument, " - "which must be an array. " - "Return the modified array `arr`.") { + "Join `parts`, a variable number of arrays and tuples, into " + "the first argument `arr` which must be an array. Returns " + "`arr`.") { int32_t i; janet_arity(argc, 1, -1); JanetArray *array = janet_getarray(argv, 0); @@ -302,10 +312,11 @@ JANET_CORE_FN(cfun_array_join, JANET_CORE_FN(cfun_array_insert, "(array/insert arr at & xs)", - "Insert all `xs` into array `arr` at index `at`. `at` should be an integer between " - "0 and the length of the array. A negative value for `at` will index backwards from " - "the end of the array, inserting after the index such that inserting at -1 appends to " - "the array. Returns the array.") { + "Insert all `xs` into array `arr` at index `at`. `at` can be " + "an integer between 0 and the length of `arr`. If `at` is " + "negative, will index backwards from the end of `arr`, " + "inserting after the index such that inserting at -1 appends " + "to `arr`. Returns `arr`.") { size_t chunksize, restsize; janet_arity(argc, 2, -1); JanetArray *array = janet_getarray(argv, 0); @@ -333,10 +344,10 @@ JANET_CORE_FN(cfun_array_insert, JANET_CORE_FN(cfun_array_remove, "(array/remove arr at &opt n)", - "Remove up to `n` elements starting at index `at` in array `arr`. `at` can index from " - "the end of the array with a negative index, and `n` must be a non-negative integer. " - "By default, `n` is 1. " - "Returns the array.") { + "Remove up to `n` values starting at index `at` in array " + "`arr`. `at` can index from the end of the array with a " + "negative index, and `n` must be a non-negative integer. " + "By default `n` is 1. Returns `arr`.") { janet_arity(argc, 2, 3); JanetArray *array = janet_getarray(argv, 0); int32_t at = janet_getinteger(argv, 1); @@ -363,7 +374,8 @@ JANET_CORE_FN(cfun_array_remove, JANET_CORE_FN(cfun_array_trim, "(array/trim arr)", - "Set the backing capacity of an array to its current length. Returns the modified array.") { + "Set the backing capacity of an array `arr` to its current " + "length. Returns `arr`.") { janet_fixarity(argc, 1); JanetArray *array = janet_getarray(argv, 0); if (array->count) { @@ -385,8 +397,8 @@ JANET_CORE_FN(cfun_array_trim, JANET_CORE_FN(cfun_array_clear, "(array/clear arr)", - "Empties an array, setting its count to 0 but does not free the backing capacity. " - "Returns the modified array.") { + "Empties an array `arr`, setting its count to 0 but does not " + "free the backing capacity. Returns `arr`.") { janet_fixarity(argc, 1); JanetArray *array = janet_getarray(argv, 0); array->count = 0; From 9f53b90dea78bd396bcc83260fa22ef3909ad07c Mon Sep 17 00:00:00 2001 From: sogaiu <33044872+sogaiu@users.noreply.github.com> Date: Sat, 12 Sep 2026 01:55:41 +0000 Subject: [PATCH 09/12] Tweak ffi/defbind-alias docstring (#1853) Co-authored-by: sogaiu <983021772@users.noreply.github.com> --- src/boot/boot.janet | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 23e87dbe..6df9cc5e 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -4355,8 +4355,8 @@ (defmacro ffi/defbind-alias :flycheck "Generate bindings for native functions in a convenient manner. - Similar to defbind but allows for the janet function name to be - different than the FFI function." + Similar to `ffi/defbind` but allows for the janet function name + to be different than the FFI function." [name alias ret-type & body] (def real-ret-type (eval ret-type)) (def meta (slice body 0 -2)) From a9625276be821f77e82f83082edec77ba3b90a57 Mon Sep 17 00:00:00 2001 From: sogaiu <33044872+sogaiu@users.noreply.github.com> Date: Sat, 12 Sep 2026 01:56:06 +0000 Subject: [PATCH 10/12] Unify assignment operator docstrings (#1857) Co-authored-by: sogaiu <983021772@users.noreply.github.com> --- src/boot/boot.janet | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 6df9cc5e..4179e06a 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -139,11 +139,11 @@ (defn dec "Returns x - 1." [x] (- x 1)) (defmacro ++ "Increments the var x by 1." [x] ~(set ,x (,+ ,x ,1))) (defmacro -- "Decrements the var x by 1." [x] ~(set ,x (,- ,x ,1))) -(defmacro += "Increments the var x by n." [x & ns] ~(set ,x (,+ ,x ,;ns))) -(defmacro -= "Decrements the var x by n." [x & ns] ~(set ,x (,- ,x ,;ns))) -(defmacro *= "Shorthand for (set x (\\* x n))." [x & ns] ~(set ,x (,* ,x ,;ns))) -(defmacro /= "Shorthand for (set x (/ x n))." [x & ns] ~(set ,x (,/ ,x ,;ns))) -(defmacro %= "Shorthand for (set x (% x n))." [x & ns] ~(set ,x (,% ,x ,;ns))) +(defmacro += "Shorthand for (set x (+ x ;ns))." [x & ns] ~(set ,x (,+ ,x ,;ns))) +(defmacro -= "Shorthand for (set x (- x ;ns))." [x & ns] ~(set ,x (,- ,x ,;ns))) +(defmacro *= "Shorthand for (set x (* x ;ns))." [x & ns] ~(set ,x (,* ,x ,;ns))) +(defmacro /= "Shorthand for (set x (/ x ;ns))." [x & ns] ~(set ,x (,/ ,x ,;ns))) +(defmacro %= "Shorthand for (set x (% x ;ns))." [x & ns] ~(set ,x (,% ,x ,;ns))) (defmacro assert :flycheck # should top level assert flycheck? "Throw an error if x is not truthy. Will not evaluate `err` if x is truthy." From 9ed76f42fb50bb4c0f6ce754da1e222dc867b34a Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Fri, 11 Sep 2026 18:56:23 -0500 Subject: [PATCH 11/12] Bytecode improvements. Add jump threading and better dead code elimination to our bytecode compiler. Also iterate it to a fixpoint. Last, add a total instruction counter during bootstrap so we can easily see how many instructions are in the core library. These simple passes were factored out of some more involved bytecode optimization work as easy wins without much code. --- src/boot/boot.c | 2 + src/boot/tests.h | 6 ++ src/core/bytecode.c | 169 +++++++++++++++++++++++++++++++++++++++++--- src/core/compile.c | 3 +- src/core/compile.h | 3 +- src/core/marsh.c | 2 +- src/core/specials.c | 6 +- src/core/util.c | 2 +- src/core/util.h | 4 ++ 9 files changed, 180 insertions(+), 17 deletions(-) diff --git a/src/boot/boot.c b/src/boot/boot.c index 3a3c502b..abda24ca 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -107,6 +107,8 @@ int main(int argc, const char **argv) { status = janet_dobytes(env, boot_buffer, (int32_t) boot_size, boot_filename, NULL); janet_free(boot_buffer); + fprintf(stderr, "total bytecode instructions created: %" PRIi64 "\n", total_instruction_count); + /* Deinitialize vm */ janet_deinit(); diff --git a/src/boot/tests.h b/src/boot/tests.h index 65e40433..10b0ca91 100644 --- a/src/boot/tests.h +++ b/src/boot/tests.h @@ -1,6 +1,12 @@ #ifndef TESTS_H_DNMBUYYL #define TESTS_H_DNMBUYYL +#include +#include + +/* Copy of util.h */ +extern int64_t total_instruction_count; + /* Tests */ extern int array_test(); extern int buffer_test(); diff --git a/src/core/bytecode.c b/src/core/bytecode.c index f1fbbe1d..af43388d 100644 --- a/src/core/bytecode.c +++ b/src/core/bytecode.c @@ -26,6 +26,7 @@ #include "gc.h" #include "util.h" #include "regalloc.h" +#include "vector.h" #endif /* Look up table for instructions */ @@ -109,6 +110,83 @@ const enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = { JINT_SSS /* JOP_CANCEL, */ }; +/* Keep track of how large corelib is. Useful + * for minimizing code bloat. */ +#ifdef JANET_BOOTSTRAP +int64_t total_instruction_count = 0; +#endif + +/* Traverse bytecode and remove unreachable code my marking it as a noop. */ +void janet_bytecode_dead_code(JanetFuncDef *def) { + int32_t *pcstack = NULL; + uint32_t *code = def->bytecode; + int32_t *codes = (int32_t *)code; + int32_t len = def->bytecode_length; + int32_t words = (len + 31) / 32; + uint32_t *visited_bitmap = array_allocate(sizeof(uint32_t), words); + memset(visited_bitmap, 0, words * sizeof(uint32_t)); + janet_v_push(pcstack, 0); + while (janet_v_count(pcstack)) { + int32_t pc = janet_v_last(pcstack); + janet_v_pop(pcstack); + while (pc < len) { + int32_t index = pc >> 5; + int32_t mask = 1 << (pc & 0x1F); + if (visited_bitmap[index] & mask) break; + visited_bitmap[index] |= mask; + switch (code[pc] & 0x7F) { + default: + pc++; + continue; + case JOP_RETURN_NIL: + case JOP_RETURN: + case JOP_ERROR: + case JOP_TAILCALL: + pc = len; + continue; + case JOP_JUMP: + janet_v_push(pcstack, pc + (codes[pc] >> 8)); + pc = len; + continue; + case JOP_JUMP_IF: + case JOP_JUMP_IF_NIL: + case JOP_JUMP_IF_NOT: + case JOP_JUMP_IF_NOT_NIL: + janet_v_push(pcstack, pc + (codes[pc] >> 16)); + pc++; + continue; + } + } + } + for (int32_t pc = 0; pc < len; pc++) { + if (!(visited_bitmap[pc >> 5] & (1 << (pc & 0x1F)))) { + code[pc] = JOP_NOOP; + } + } + janet_free(visited_bitmap); + janet_v_free(pcstack); +} + +/* Handle remapping symbols when bytecode changes */ +static void rewrite_symbolmap(JanetFuncDef *def, uint32_t *pc_map) { + int32_t smout = 0; + for (int32_t i = 0; i < def->symbolmap_length; i++) { + JanetSymbolMap *sm = def->symbolmap + i; + int keep = 1; + /* Don't rewrite upvalue mappings */ + if (sm->birth_pc < UINT32_MAX) { + sm->birth_pc = pc_map[sm->birth_pc]; + sm->death_pc = pc_map[sm->death_pc]; + /* entirely dead symbols can be removed from the symbol map. This can happen if a symbol is in dead code. */ + if (sm->death_pc > (uint32_t) def->bytecode_length) sm->death_pc = (uint32_t) def->bytecode_length; + if (sm->birth_pc >= sm->death_pc) keep = 0; + } + /* Now shift if needed */ + if (keep) def->symbolmap[smout++] = *sm; + } + def->symbolmap_length = smout; +} + /* Remove all noops while preserving jumps and debugging information. * Useful as part of a filtering compiler pass. */ void janet_bytecode_remove_noops(JanetFuncDef *def) { @@ -166,23 +244,73 @@ void janet_bytecode_remove_noops(JanetFuncDef *def) { } /* Rewrite symbolmap */ - for (int32_t i = 0; i < def->symbolmap_length; i++) { - JanetSymbolMap *sm = def->symbolmap + i; - /* Don't rewrite upvalue mappings */ - if (sm->birth_pc < UINT32_MAX) { - sm->birth_pc = pc_map[sm->birth_pc]; - sm->death_pc = pc_map[sm->death_pc]; - } - } + rewrite_symbolmap(def, pc_map); def->bytecode_length = new_bytecode_length; def->bytecode = janet_realloc(def->bytecode, def->bytecode_length * sizeof(uint32_t)); janet_sfree(pc_map); } +/* Simple Jump threading. Traverse basic blocks instead? + * jump instructions are always last instruction in basic block. */ +void janet_bytecode_jump_threading(JanetFuncDef *def) { + int32_t blen = def->bytecode_length; + uint32_t *code = def->bytecode; + int32_t *codes = (int32_t *)code; + int recur = 1; + while (recur) { + recur = 0; + for (int32_t i = 0; i < blen; i++) { + int32_t target; + int is_branch = 0; + switch (code[i] & 0x7F) { + default: + continue; + case JOP_JUMP: + target = i + (codes[i] >> 8); + break; + case JOP_JUMP_IF: + case JOP_JUMP_IF_NOT: + case JOP_JUMP_IF_NIL: + case JOP_JUMP_IF_NOT_NIL: + is_branch = 1; + target = i + (codes[i] >> 16); + break; + } + if (target == i) continue; /* infinite loop */ + if (target == i + 1) { + code[i] = JOP_NOOP; + recur = 1; + continue; + } + while ((code[target] & 0x7F) == JOP_NOOP) { /* Skip noops */ + target++; + } + if ((code[target] & 0x7F) == JOP_RETURN_NIL && !is_branch) { + code[i] = JOP_RETURN_NIL; + recur = 1; + continue; + } + /* update target */ + if ((code[target] & 0x7F) == JOP_JUMP) { + target += (codes[target] >> 8); + } + uint32_t newcode; + if (is_branch) { + newcode = (code[i] & 0xFFFF) | ((uint32_t)(target - i) << 16); + } else { + newcode = (code[i] & 0xFF) | ((uint32_t)(target - i) << 8); + } + if (newcode != code[i]) recur = 1; + code[i] = newcode; + } + } +} + /* Remove redundant loads, moves and other instructions if possible and convert them to * noops. Input is assumed valid bytecode. */ void janet_bytecode_movopt(JanetFuncDef *def) { + JanetcRegisterAllocator ra; int recur = 1; @@ -357,6 +485,8 @@ void janet_bytecode_movopt(JanetFuncDef *def) { case JOP_LOAD_TRUE: case JOP_LOAD_FALSE: case JOP_LOAD_SELF: + case JOP_MAKE_STRING: + case JOP_MAKE_TABLE: case JOP_MAKE_ARRAY: case JOP_MAKE_TUPLE: case JOP_MAKE_BRACKET_TUPLE: { @@ -401,6 +531,29 @@ void janet_bytecode_movopt(JanetFuncDef *def) { } } +/* Entry point for optimization */ +void janet_bytecode_optimize(JanetFuncDef *def) { + int32_t delta; + do { // Fixpoint for instruction removal + int32_t before = def->bytecode_length; + janet_bytecode_jump_threading(def); + janet_bytecode_movopt(def); + janet_bytecode_dead_code(def); + janet_bytecode_remove_noops(def); + delta = def->bytecode_length - before; + } while (delta < 0); +#ifdef JANET_BOOTSTRAP + total_instruction_count += def->bytecode_length; +#endif +} + +/* TODO - type and error analysis */ +/* +void janet_bytecode_analyze(JanetFuncDef *def) { + janet_panic("NYI"); +} +*/ + /* Verify some bytecode */ int janet_verify(JanetFuncDef *def) { int vargs = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG); diff --git a/src/core/compile.c b/src/core/compile.c index 1ded7d26..a9607a66 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1131,8 +1131,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) { janetc_popscope(c); /* Do basic optimization */ - janet_bytecode_movopt(def); - janet_bytecode_remove_noops(def); + janet_bytecode_optimize(def); return def; } diff --git a/src/core/compile.h b/src/core/compile.h index 9f120013..6983dd83 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -288,7 +288,6 @@ JanetSlot janetc_resolve(JanetCompiler *c, const uint8_t *sym); Shadowing janetc_shadowcheck(JanetCompiler *c, const uint8_t *sym); /* Bytecode optimization */ -void janet_bytecode_movopt(JanetFuncDef *def); -void janet_bytecode_remove_noops(JanetFuncDef *def); +void janet_bytecode_optimize(JanetFuncDef *def); #endif diff --git a/src/core/marsh.c b/src/core/marsh.c index 043fa8c1..07288a55 100644 --- a/src/core/marsh.c +++ b/src/core/marsh.c @@ -1484,7 +1484,7 @@ static const uint8_t *unmarshal_one( /* Tuple */ Janet *tup = janet_tuple_begin(len); int32_t flag = readint(st, &data); - janet_tuple_flag(tup) |= (int32_t) (((uint32_t) flag) << 16); /* Avoid left shift of negative value */ + janet_tuple_flag(tup) |= (int32_t)(((uint32_t) flag) << 16); /* Avoid left shift of negative value */ for (int32_t i = 0; i < len; i++) { data = unmarshal_one(st, data, tup + i, flags + 1); } diff --git a/src/core/specials.c b/src/core/specials.c index 1f154d8b..102ba5dc 100644 --- a/src/core/specials.c +++ b/src/core/specials.c @@ -734,10 +734,10 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) { labeld = janet_v_count(c->buffer); if (labeljr < labeld) { check_16bit_jump(c, labeljr, labelr); - c->buffer[labeljr] |= (uint32_t) (labelr - labeljr) << 16; + c->buffer[labeljr] |= (uint32_t)(labelr - labeljr) << 16; if (!tail && labeljd < labeld) { check_24bit_jump(c, labeljd, labeld); - c->buffer[labeljd] |= (uint32_t) (labeld - labeljd) << 8; + c->buffer[labeljd] |= (uint32_t)(labeld - labeljd) << 8; } } @@ -991,7 +991,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv) for (int32_t i = labelwt; i < labeld; i++) { if (c->buffer[i] == (0x80 | JOP_JUMP)) { check_24bit_jump(c, i, labeld); - c->buffer[i] = JOP_JUMP | ((uint32_t) (labeld - i) << 8); + c->buffer[i] = JOP_JUMP | ((uint32_t)(labeld - i) << 8); } } diff --git a/src/core/util.c b/src/core/util.c index 1156081e..c678703d 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -584,7 +584,7 @@ static void janet_check_pointer_align(void *p) { uintptr_t u; } un; un.p = p; - janet_assert(!(un.u & (uintptr_t) ((1 << JANET_NANBOX_64_POINTER_SHIFT) - 1)), + 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 } diff --git a/src/core/util.h b/src/core/util.h index 101fc27d..50177bff 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -167,6 +167,10 @@ enum JanetTimeSource { int janet_gettime(struct timespec *spec, enum JanetTimeSource source); #endif +#ifdef JANET_BOOTSTRAP +extern int64_t total_instruction_count; +#endif + /* strdup */ #ifdef JANET_WINDOWS #define strdup(x) _strdup(x) From f3b8fd9ff193a9789eecb8b4bf762e95dcc6ac0f Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 12 Sep 2026 11:30:04 -0500 Subject: [PATCH 12/12] Remove memset use in net.c. MSVC overlaps the memory of variables and does some odd things here that reasonable compiler tooldchains do not. --- src/core/net.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/net.c b/src/core/net.c index a1baa9a0..7121715d 100644 --- a/src/core/net.c +++ b/src/core/net.c @@ -947,9 +947,8 @@ JANET_CORE_FN(cfun_net_getsockname, janet_fixarity(argc, 1); JanetStream *js = janet_getabstract(argv, 0, &janet_stream_type); if (js->flags & JANET_STREAM_CLOSED) janet_panic("stream closed"); - struct sockaddr_storage ss; + struct sockaddr_storage ss = { 0 }; socklen_t slen = sizeof(ss); - memset(&ss, 0, slen); if (getsockname((JSock)js->handle, (struct sockaddr *) &ss, &slen)) { janet_panicf("Failed to get localname on %v: %V", argv[0], janet_ev_lasterr()); } @@ -963,9 +962,8 @@ JANET_CORE_FN(cfun_net_getpeername, janet_fixarity(argc, 1); JanetStream *js = janet_getabstract(argv, 0, &janet_stream_type); if (js->flags & JANET_STREAM_CLOSED) janet_panic("stream closed"); - struct sockaddr_storage ss; + struct sockaddr_storage ss = { 0 }; socklen_t slen = sizeof(ss); - memset(&ss, 0, slen); if (getpeername((JSock)js->handle, (struct sockaddr *)&ss, &slen)) { janet_panicf("Failed to get peername on %v: %V", argv[0], janet_ev_lasterr()); }