mirror of
https://github.com/janet-lang/janet
synced 2026-04-03 21:41:25 +00:00
Compare commits
13 Commits
connect-ex
...
contributi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d816011ec1 | ||
|
|
934cf9d5b8 | ||
|
|
9880475262 | ||
|
|
bab71feadb | ||
|
|
8eae8984b1 | ||
|
|
df56efbae0 | ||
|
|
b160f4f5c0 | ||
|
|
a0cc867f14 | ||
|
|
8f446736ed | ||
|
|
decd7078af | ||
|
|
b96350f132 | ||
|
|
aa63adccb4 | ||
|
|
7fc12ff167 |
21
.github/workflows/test.yml
vendored
21
.github/workflows/test.yml
vendored
@@ -56,6 +56,27 @@ 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:
|
||||
|
||||
@@ -3,6 +3,8 @@ 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.
|
||||
|
||||
@@ -37,6 +37,12 @@ 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
|
||||
@@ -90,3 +96,18 @@ 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.
|
||||
|
||||
@@ -23,7 +23,17 @@
|
||||
@rem set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD /fsanitize=address /Zi
|
||||
@rem set JANET_LINK=link /nologo clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
|
||||
|
||||
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD
|
||||
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_LINK=link /nologo
|
||||
|
||||
@set JANET_LINK_STATIC=lib /nologo
|
||||
|
||||
@@ -443,11 +443,36 @@
|
||||
(def ,binding ,ctor)
|
||||
,(defer-impl :with [(or dtor :close) binding] body)))
|
||||
|
||||
# declare ahead of time
|
||||
(var- macexvar nil)
|
||||
|
||||
(defmacro if-let
|
||||
``Make multiple bindings, and if all are truthy,
|
||||
evaluate the `tru` form. If any are false or nil, evaluate
|
||||
the `fal` form. Bindings have the same syntax as the `let` macro.``
|
||||
[bindings tru &opt fal]
|
||||
(def len (length bindings))
|
||||
(if (= 0 len) (error "expected at least 1 binding"))
|
||||
(if (odd? len) (error "expected an even number of bindings"))
|
||||
(def fal2 (if macexvar (macexvar fal) fal))
|
||||
(defn aux [i]
|
||||
(if (>= i len)
|
||||
tru
|
||||
(do
|
||||
(def bl (in bindings i))
|
||||
(def br (in bindings (+ 1 i)))
|
||||
(if (symbol? bl)
|
||||
~(if (def ,bl ,br) ,(aux (+ 2 i)) ,fal2)
|
||||
~(if (def ,(def sym (gensym)) ,br)
|
||||
(do (def ,bl ,sym) ,(aux (+ 2 i)))
|
||||
,fal2)))))
|
||||
(aux 0))
|
||||
|
||||
(defmacro when-with
|
||||
``Similar to with, but if binding is false or nil, returns
|
||||
nil without evaluating the body. Otherwise, the same as `with`.``
|
||||
[[binding ctor dtor] & body]
|
||||
~(if-let [,binding ,ctor]
|
||||
~(as-macro ,if-let [,binding ,ctor]
|
||||
,(defer-impl :when-with [(or dtor :close) binding] body)))
|
||||
|
||||
(defmacro if-with
|
||||
@@ -455,7 +480,7 @@
|
||||
the falsey path. Otherwise, evaluates the truthy path. In both cases,
|
||||
`ctor` is bound to binding.``
|
||||
[[binding ctor dtor] truthy &opt falsey]
|
||||
~(if-let [,binding ,ctor]
|
||||
~(as-macro ,if-let [,binding ,ctor]
|
||||
,(defer-impl :if-with [(or dtor :close) binding] [truthy])
|
||||
,falsey))
|
||||
|
||||
@@ -539,13 +564,13 @@
|
||||
(case binding
|
||||
:until ~(do (if ,verb (break) nil) ,rest)
|
||||
:while ~(do (if ,verb nil (break)) ,rest)
|
||||
:let ~(let ,verb (do ,rest))
|
||||
:let ~(as-macro ,let ,verb (do ,rest))
|
||||
:after ~(do ,rest ,verb nil)
|
||||
:before ~(do ,verb ,rest nil)
|
||||
:repeat (with-syms [iter]
|
||||
~(do (var ,iter ,verb) (while (> ,iter 0) ,rest (-- ,iter))))
|
||||
:when ~(when ,verb ,rest)
|
||||
:unless ~(unless ,verb ,rest)
|
||||
~(do (var ,iter ,verb) (while (,> ,iter 0) ,rest (as-macro ,-- ,iter))))
|
||||
:when ~(as-macro ,when ,verb ,rest)
|
||||
:unless ~(as-macro ,unless ,verb ,rest)
|
||||
(error (string "unexpected loop modifier " binding))))))
|
||||
|
||||
# 3 term expression
|
||||
@@ -587,7 +612,7 @@
|
||||
"Evaluate body n times. If n is negative, body will be evaluated 0 times. Evaluates to nil."
|
||||
[n & body]
|
||||
(with-syms [iter]
|
||||
~(do (var ,iter ,n) (while (> ,iter 0) ,;body (-- ,iter)))))
|
||||
~(do (var ,iter ,n) (while (,> ,iter 0) ,;body (as-macro ,-- ,iter)))))
|
||||
|
||||
(defmacro forever
|
||||
"Evaluate body forever in a loop, or until a break statement."
|
||||
@@ -683,7 +708,7 @@
|
||||
[head & body]
|
||||
(def $accum (gensym))
|
||||
(check-empty-body body)
|
||||
~(do (def ,$accum @[]) (loop ,head (,array/push ,$accum (do ,;body))) ,$accum))
|
||||
~(do (def ,$accum @[]) (as-macro ,loop ,head (,array/push ,$accum (do ,;body))) ,$accum))
|
||||
|
||||
(defmacro catseq
|
||||
``Similar to `loop`, but concatenates each element from the loop body into an array and returns that.
|
||||
@@ -691,21 +716,21 @@
|
||||
[head & body]
|
||||
(def $accum (gensym))
|
||||
(check-empty-body body)
|
||||
~(do (def ,$accum @[]) (loop ,head (,array/concat ,$accum (do ,;body))) ,$accum))
|
||||
~(do (def ,$accum @[]) (as-macro ,loop ,head (,array/concat ,$accum (do ,;body))) ,$accum))
|
||||
|
||||
(defmacro tabseq
|
||||
``Similar to `loop`, but accumulates key value pairs into a table.
|
||||
See `loop` for details.``
|
||||
[head key-body & value-body]
|
||||
(def $accum (gensym))
|
||||
~(do (def ,$accum @{}) (loop ,head (,put ,$accum ,key-body (do ,;value-body))) ,$accum))
|
||||
~(do (def ,$accum @{}) (as-macro ,loop ,head (,put ,$accum ,key-body (do ,;value-body))) ,$accum))
|
||||
|
||||
(defmacro generate
|
||||
``Create a generator expression using the `loop` syntax. Returns a fiber
|
||||
that yields all values inside the loop in order. See `loop` for details.``
|
||||
[head & body]
|
||||
(check-empty-body body)
|
||||
~(,fiber/new (fn :generate [] (loop ,head (yield (do ,;body)))) :yi))
|
||||
~(,fiber/new (fn :generate [] (as-macro ,loop ,head (,yield (do ,;body)))) :yi))
|
||||
|
||||
(defmacro coro
|
||||
"A wrapper for making fibers that may yield multiple values (coroutine). Same as `(fiber/new (fn [] ;body) :yi)`."
|
||||
@@ -754,35 +779,10 @@
|
||||
(each x xs (*= accum x))
|
||||
accum)
|
||||
|
||||
# declare ahead of time
|
||||
(var- macexvar nil)
|
||||
|
||||
(defmacro if-let
|
||||
``Make multiple bindings, and if all are truthy,
|
||||
evaluate the `tru` form. If any are false or nil, evaluate
|
||||
the `fal` form. Bindings have the same syntax as the `let` macro.``
|
||||
[bindings tru &opt fal]
|
||||
(def len (length bindings))
|
||||
(if (= 0 len) (error "expected at least 1 binding"))
|
||||
(if (odd? len) (error "expected an even number of bindings"))
|
||||
(def fal2 (if macexvar (macexvar fal) fal))
|
||||
(defn aux [i]
|
||||
(if (>= i len)
|
||||
tru
|
||||
(do
|
||||
(def bl (in bindings i))
|
||||
(def br (in bindings (+ 1 i)))
|
||||
(if (symbol? bl)
|
||||
~(if (def ,bl ,br) ,(aux (+ 2 i)) ,fal2)
|
||||
~(if (def ,(def sym (gensym)) ,br)
|
||||
(do (def ,bl ,sym) ,(aux (+ 2 i)))
|
||||
,fal2)))))
|
||||
(aux 0))
|
||||
|
||||
(defmacro when-let
|
||||
"Same as `(if-let bindings (do ;body))`."
|
||||
[bindings & body]
|
||||
~(if-let ,bindings (do ,;body)))
|
||||
~(as-macro ,if-let ,bindings (do ,;body)))
|
||||
|
||||
(defn comp
|
||||
`Takes multiple functions and returns a function that is the composition
|
||||
@@ -1432,7 +1432,7 @@
|
||||
(tuple n @[])))
|
||||
(def sym (gensym))
|
||||
(def parts (array/concat @[h sym] t))
|
||||
~(let [,sym ,last] (if ,sym ,(keep-syntax! n parts))))
|
||||
~(as-macro ,let [,sym ,last] (if ,sym ,(keep-syntax! n parts))))
|
||||
(reduce fop x forms))
|
||||
|
||||
(defmacro -?>>
|
||||
@@ -1448,7 +1448,7 @@
|
||||
(tuple n @[])))
|
||||
(def sym (gensym))
|
||||
(def parts (array/concat @[h] t @[sym]))
|
||||
~(let [,sym ,last] (if ,sym ,(keep-syntax! n parts))))
|
||||
~(as-macro ,let [,sym ,last] (if ,sym ,(keep-syntax! n parts))))
|
||||
(reduce fop x forms))
|
||||
|
||||
(defn- walk-ind [f form]
|
||||
@@ -2411,8 +2411,8 @@
|
||||
(dictionary? m) (merge-into metadata m)
|
||||
(error (string "invalid metadata " m))))
|
||||
(with-syms [entry old-entry f]
|
||||
~(let [,old-entry (,dyn ',name)]
|
||||
(def ,entry (or ,old-entry @{:ref @[nil]}))
|
||||
~(as-macro ,let [,old-entry (,dyn ',name)]
|
||||
(def ,entry (as-macro ,or ,old-entry @{:ref @[nil]}))
|
||||
(,setdyn ',name ,entry)
|
||||
(def ,f ,fbody)
|
||||
(,put-in ,entry [:ref 0] ,f)
|
||||
@@ -3953,7 +3953,7 @@
|
||||
``
|
||||
[sec & body]
|
||||
(with-syms [f]
|
||||
~(let [,f (coro ,;body)]
|
||||
~(as-macro ,let [,f (as-macro ,coro ,;body)]
|
||||
(,ev/deadline ,sec nil ,f)
|
||||
(,resume ,f))))
|
||||
|
||||
@@ -4085,15 +4085,15 @@
|
||||
(defn make-ptr []
|
||||
(assertf (ffi/lookup (if lazy (llib) lib) raw-symbol) "failed to find ffi symbol %v" raw-symbol))
|
||||
(if lazy
|
||||
~(defn ,alias ,;meta [,;formal-args]
|
||||
~(as-macro ,defn ,alias ,;meta [,;formal-args]
|
||||
(,ffi/call (,(delay (make-ptr))) (,(delay (make-sig))) ,;formal-args))
|
||||
~(defn ,alias ,;meta [,;formal-args]
|
||||
~(as-macro ,defn ,alias ,;meta [,;formal-args]
|
||||
(,ffi/call ,(make-ptr) ,(make-sig) ,;formal-args))))
|
||||
|
||||
(defmacro ffi/defbind :flycheck
|
||||
"Generate bindings for native functions in a convenient manner."
|
||||
[name ret-type & body]
|
||||
~(ffi/defbind-alias ,name ,name ,ret-type ,;body)))
|
||||
~(as-macro ,ffi/defbind-alias ,name ,name ,ret-type ,;body)))
|
||||
|
||||
###
|
||||
###
|
||||
|
||||
@@ -1110,6 +1110,7 @@ JANET_CORE_FN(cfun_disasm,
|
||||
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);
|
||||
|
||||
@@ -1172,7 +1172,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, 0);
|
||||
janet_chan_init(channel, (int32_t) limit, 1);
|
||||
return channel;
|
||||
}
|
||||
|
||||
@@ -1712,20 +1712,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 == overlapped) {
|
||||
if (stream->read_fiber && stream->read_fiber->ev_state == jo) {
|
||||
fiber = stream->read_fiber;
|
||||
} else if (stream->write_fiber && stream->write_fiber->ev_state == overlapped) {
|
||||
} else if (stream->write_fiber && stream->write_fiber->ev_state == jo) {
|
||||
fiber = stream->write_fiber;
|
||||
}
|
||||
if (fiber != NULL) {
|
||||
fiber->flags &= ~JANET_FIBER_EV_FLAG_IN_FLIGHT;
|
||||
/* System is done with this, we can reused this data */
|
||||
overlapped->InternalHigh = (ULONG_PTR) num_bytes_transferred;
|
||||
jo->bytes_transfered = (ULONG_PTR) num_bytes_transferred;
|
||||
fiber->ev_callback(fiber, result ? JANET_ASYNC_EVENT_COMPLETE : JANET_ASYNC_EVENT_FAILED);
|
||||
} else {
|
||||
janet_free((void *) overlapped);
|
||||
janet_free((void *) jo);
|
||||
janet_ev_dec_refcount();
|
||||
}
|
||||
janet_stream_checktoclose(stream);
|
||||
@@ -2437,7 +2437,7 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
#ifdef JANET_WINDOWS
|
||||
OVERLAPPED overlapped;
|
||||
JanetOverlapped overlapped;
|
||||
DWORD flags;
|
||||
#ifdef JANET_NET
|
||||
WSABUF wbuf;
|
||||
@@ -2472,7 +2472,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.InternalHigh;
|
||||
uint32_t ev_bytes = (uint32_t) state->overlapped.bytes_transfered;
|
||||
state->bytes_read += ev_bytes;
|
||||
if (state->bytes_read == 0 && (state->mode != JANET_ASYNC_READMODE_RECVFROM)) {
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
@@ -2504,7 +2504,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(OVERLAPPED));
|
||||
memset(&(state->overlapped), 0, sizeof(JanetOverlapped));
|
||||
int status;
|
||||
#ifdef JANET_NET
|
||||
if (state->mode == JANET_ASYNC_READMODE_RECVFROM) {
|
||||
@@ -2512,7 +2512,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, NULL);
|
||||
NULL, &state->flags, &state->from, &state->fromlen, &state->overlapped.as.wsaoverlapped, NULL);
|
||||
if (status && (WSA_IO_PENDING != WSAGetLastError())) {
|
||||
janet_cancel(fiber, janet_ev_lasterr());
|
||||
janet_async_end(fiber);
|
||||
@@ -2523,9 +2523,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.Offset = (DWORD) state->bytes_read;
|
||||
state->overlapped.as.overlapped.Offset = (DWORD) state->bytes_read;
|
||||
|
||||
status = ReadFile(stream->handle, state->chunk_buf, chunk_size, NULL, &state->overlapped);
|
||||
status = ReadFile(stream->handle, state->chunk_buf, chunk_size, NULL, &state->overlapped.as.overlapped);
|
||||
if (!status && (ERROR_IO_PENDING != GetLastError())) {
|
||||
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
||||
if (state->bytes_read) {
|
||||
@@ -2681,7 +2681,7 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
#ifdef JANET_WINDOWS
|
||||
OVERLAPPED overlapped;
|
||||
JanetOverlapped overlapped;
|
||||
DWORD flags;
|
||||
#ifdef JANET_NET
|
||||
WSABUF wbuf;
|
||||
@@ -2722,7 +2722,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.InternalHigh;
|
||||
uint32_t ev_bytes = (uint32_t) state->overlapped.bytes_transfered;
|
||||
if (ev_bytes == 0 && (state->mode != JANET_ASYNC_WRITEMODE_SENDTO)) {
|
||||
janet_cancel(fiber, janet_cstringv("disconnect"));
|
||||
janet_async_end(fiber);
|
||||
@@ -2751,7 +2751,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
bytes = state->src.str;
|
||||
len = janet_string_length(bytes);
|
||||
}
|
||||
memset(&(state->overlapped), 0, sizeof(WSAOVERLAPPED));
|
||||
memset(&(state->overlapped), 0, sizeof(JanetOverlapped));
|
||||
|
||||
int status;
|
||||
#ifdef JANET_NET
|
||||
@@ -2761,7 +2761,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, NULL);
|
||||
status = WSASendTo(sock, &state->wbuf, 1, NULL, state->flags, to, tolen, &state->overlapped.as.wsaoverlapped, NULL);
|
||||
if (status) {
|
||||
if (WSA_IO_PENDING == WSAGetLastError()) {
|
||||
janet_async_in_flight(fiber);
|
||||
@@ -2784,9 +2784,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.Offset = (DWORD) 0xFFFFFFFF;
|
||||
state->overlapped.OffsetHigh = (DWORD) 0xFFFFFFFF;
|
||||
status = WriteFile(stream->handle, bytes, len, NULL, &state->overlapped);
|
||||
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);
|
||||
if (!status) {
|
||||
if (ERROR_IO_PENDING == GetLastError()) {
|
||||
janet_async_in_flight(fiber);
|
||||
|
||||
@@ -326,7 +326,7 @@ static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uin
|
||||
#define FILE_INFO_PADDING (4096 * 4)
|
||||
|
||||
typedef struct {
|
||||
OVERLAPPED overlapped;
|
||||
JanetOverlapped 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.hEvent = CreateEvent(NULL, FALSE, 0, NULL); /* Do we need this */
|
||||
ow->overlapped.as.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) {
|
||||
|
||||
@@ -140,6 +140,35 @@ static int net_get_address_family(Janet x) {
|
||||
}
|
||||
|
||||
/* 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;
|
||||
@@ -159,15 +188,21 @@ 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_ERROR, (char *)&res, &size);
|
||||
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;
|
||||
#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 == 0) {
|
||||
if (r == no_error) {
|
||||
if (res == 0) {
|
||||
janet_schedule(fiber, janet_wrap_abstract(stream));
|
||||
} else {
|
||||
@@ -181,8 +216,8 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
janet_async_end(fiber);
|
||||
}
|
||||
|
||||
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
|
||||
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, NULL);
|
||||
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream, void *state) {
|
||||
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, state);
|
||||
}
|
||||
|
||||
/* State machine for accepting connections. */
|
||||
@@ -190,7 +225,7 @@ static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
|
||||
#ifdef JANET_WINDOWS
|
||||
|
||||
typedef struct {
|
||||
WSAOVERLAPPED overlapped;
|
||||
JanetOverlapped overlapped;
|
||||
JanetFunction *function;
|
||||
JanetStream *lstream;
|
||||
JanetStream *astream;
|
||||
@@ -253,7 +288,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(WSAOVERLAPPED));
|
||||
memset(&state->overlapped, 0, sizeof(JanetOverlapped));
|
||||
memset(&state->buf, 0, 1024);
|
||||
state->function = fun;
|
||||
state->lstream = stream;
|
||||
@@ -274,7 +309,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)) {
|
||||
if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped.as.wsaoverlapped)) {
|
||||
int code = WSAGetLastError();
|
||||
if (code == WSA_IO_PENDING) {
|
||||
/* indicates io is happening async */
|
||||
@@ -572,11 +607,39 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
|
||||
/* Connect to socket */
|
||||
#ifdef JANET_WINDOWS
|
||||
int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
|
||||
int err = WSAGetLastError();
|
||||
freeaddrinfo(ai);
|
||||
/* Set up the socket for non-blocking IO after connecting on windows by default */
|
||||
janet_net_socknoblock(sock);
|
||||
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);
|
||||
}
|
||||
|
||||
#else
|
||||
/* Set up the socket for non-blocking IO before connecting */
|
||||
janet_net_socknoblock(sock);
|
||||
@@ -613,7 +676,7 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
}
|
||||
}
|
||||
|
||||
net_sched_connect(stream);
|
||||
net_sched_connect(stream, NULL);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_net_socket,
|
||||
@@ -1213,6 +1276,8 @@ 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
|
||||
}
|
||||
|
||||
|
||||
105
src/core/pp.c
105
src/core/pp.c
@@ -72,7 +72,7 @@ static int count_dig10(int32_t x) {
|
||||
}
|
||||
}
|
||||
|
||||
static void integer_to_string_b(JanetBuffer *buffer, int32_t x) {
|
||||
static int32_t 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 void integer_to_string_b(JanetBuffer *buffer, int32_t x) {
|
||||
if (x == 0) {
|
||||
buf[0] = '0';
|
||||
buffer->count++;
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
if (x > 0) {
|
||||
x = -x;
|
||||
@@ -96,6 +96,7 @@ static void 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)])
|
||||
@@ -134,43 +135,55 @@ static void string_description_b(JanetBuffer *buffer, const char *title, void *p
|
||||
#undef POINTSIZE
|
||||
}
|
||||
|
||||
static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int32_t len) {
|
||||
static int 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) {
|
||||
@@ -180,13 +193,16 @@ static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, in
|
||||
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) {
|
||||
@@ -358,7 +374,7 @@ const uint8_t *janet_to_string(Janet x) {
|
||||
struct pretty {
|
||||
JanetBuffer *buffer;
|
||||
int depth;
|
||||
int indent;
|
||||
int align;
|
||||
int flags;
|
||||
int32_t bufstartlen;
|
||||
int32_t *keysort_buffer;
|
||||
@@ -450,14 +466,15 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_newline(struct pretty *S, int just_a_space) {
|
||||
static void print_newline(struct pretty *S, int align) {
|
||||
int i;
|
||||
if (just_a_space || (S->flags & JANET_PRETTY_ONELINE)) {
|
||||
S->align = align;
|
||||
if (S->flags & JANET_PRETTY_ONELINE) {
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
return;
|
||||
}
|
||||
janet_buffer_push_u8(S->buffer, '\n');
|
||||
for (i = 0; i < S->indent; i++) {
|
||||
for (i = 0; i < S->align; i++) {
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
}
|
||||
}
|
||||
@@ -484,14 +501,12 @@ 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, int is_dict_value) {
|
||||
static void janet_pretty_one(struct pretty *S, Janet x) {
|
||||
/* Add to seen */
|
||||
switch (janet_type(x)) {
|
||||
case JANET_NIL:
|
||||
@@ -506,7 +521,7 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
janet_buffer_push_cstring(S->buffer, janet_cycle_color);
|
||||
}
|
||||
janet_buffer_push_cstring(S->buffer, "<cycle ");
|
||||
integer_to_string_b(S->buffer, janet_unwrap_integer(seenid));
|
||||
S->align += 8 + 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");
|
||||
@@ -528,9 +543,11 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
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, '@');
|
||||
janet_escape_string_impl(S->buffer, S->buffer->data, S->bufstartlen);
|
||||
S->align += 1 + 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");
|
||||
@@ -547,35 +564,34 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
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, 0);
|
||||
janet_pretty_one(S, arr[i], 0);
|
||||
if (i) print_newline(S, align);
|
||||
janet_pretty_one(S, arr[i]);
|
||||
}
|
||||
print_newline(S, 0);
|
||||
print_newline(S, align);
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
for (i = 0; i < 3; i++) {
|
||||
print_newline(S, 0);
|
||||
janet_pretty_one(S, arr[len - 3 + i], 0);
|
||||
S->align += 3;
|
||||
for (i = len - 3; i < len; i++) {
|
||||
print_newline(S, align);
|
||||
janet_pretty_one(S, arr[i]);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i) print_newline(S, len < JANET_PRETTY_IND_ONELINE);
|
||||
janet_pretty_one(S, arr[i], 0);
|
||||
if (i) print_newline(S, align);
|
||||
janet_pretty_one(S, arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
S->indent -= 2;
|
||||
S->depth++;
|
||||
janet_buffer_push_u8(S->buffer, endchar);
|
||||
S->align++;
|
||||
break;
|
||||
}
|
||||
case JANET_STRUCT:
|
||||
@@ -586,6 +602,7 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
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"));
|
||||
@@ -596,6 +613,7 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
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");
|
||||
}
|
||||
@@ -613,25 +631,24 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
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_cstring(S->buffer, "{");
|
||||
janet_buffer_push_u8(S->buffer, '{');
|
||||
const int align = ++S->align;
|
||||
|
||||
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;
|
||||
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;
|
||||
int truncated = 0;
|
||||
|
||||
@@ -644,15 +661,17 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
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, len < JANET_PRETTY_DICT_ONELINE);
|
||||
janet_pretty_one(S, kvs[j].key, 0);
|
||||
if (i) print_newline(S, align);
|
||||
janet_pretty_one(S, kvs[j].key);
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
janet_pretty_one(S, kvs[j].value, 1);
|
||||
S->align++;
|
||||
janet_pretty_one(S, kvs[j].value);
|
||||
j++;
|
||||
}
|
||||
if (truncated) {
|
||||
print_newline(S, 0);
|
||||
print_newline(S, align);
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
S->align += 3;
|
||||
}
|
||||
} else {
|
||||
/* Sorted keys dictionaries */
|
||||
@@ -685,24 +704,26 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
if (i) print_newline(S, len < JANET_PRETTY_DICT_ONELINE);
|
||||
if (i) print_newline(S, align);
|
||||
int32_t j = S->keysort_buffer[i + ks_start];
|
||||
janet_pretty_one(S, kvs[j].key, 0);
|
||||
janet_pretty_one(S, kvs[j].key);
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
janet_pretty_one(S, kvs[j].value, 1);
|
||||
S->align++;
|
||||
janet_pretty_one(S, kvs[j].value);
|
||||
}
|
||||
|
||||
if (truncated) {
|
||||
print_newline(S, 0);
|
||||
print_newline(S, align);
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
S->align += 3;
|
||||
}
|
||||
|
||||
}
|
||||
S->keysort_start = ks_start;
|
||||
}
|
||||
S->indent -= 2;
|
||||
S->depth++;
|
||||
janet_buffer_push_u8(S->buffer, '}');
|
||||
S->align++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -718,14 +739,14 @@ static JanetBuffer *janet_pretty_(JanetBuffer *buffer, int depth, int flags, Jan
|
||||
}
|
||||
S.buffer = buffer;
|
||||
S.depth = depth;
|
||||
S.indent = 0;
|
||||
S.align = 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, 0);
|
||||
janet_pretty_one(&S, x);
|
||||
janet_table_deinit(&S.seen);
|
||||
return S.buffer;
|
||||
}
|
||||
@@ -743,7 +764,7 @@ static JanetBuffer *janet_jdn_(JanetBuffer *buffer, int depth, Janet x, int32_t
|
||||
}
|
||||
S.buffer = buffer;
|
||||
S.depth = depth;
|
||||
S.indent = 0;
|
||||
S.align = 0;
|
||||
S.flags = 0;
|
||||
S.bufstartlen = startlen;
|
||||
S.keysort_capacity = 0;
|
||||
|
||||
@@ -182,6 +182,8 @@ 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];
|
||||
|
||||
@@ -203,6 +203,21 @@ char *get_processed_name(const char *name);
|
||||
#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);
|
||||
void janet_lib_math(JanetTable *env);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <janet.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@@ -362,33 +363,50 @@ 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 ((gbl_plen + _pos) >= gbl_cols) {
|
||||
|
||||
while ((_plen + _pos) >= gbl_cols) {
|
||||
_buf++;
|
||||
_len--;
|
||||
_pos--;
|
||||
}
|
||||
while ((gbl_plen + _len) > gbl_cols) {
|
||||
|
||||
while ((_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_cstring(&b, gbl_prompt);
|
||||
janet_buffer_push_bytes(&b, (uint8_t *) _buf, _len);
|
||||
janet_buffer_push_bytes(&b, (const uint8_t *) gbl_prompt, _plen);
|
||||
if (_len > 0) {
|
||||
janet_buffer_push_bytes(&b, (uint8_t *) _buf, _len);
|
||||
}
|
||||
/* Erase to right */
|
||||
janet_buffer_push_cstring(&b, "\x1b[0K\r");
|
||||
/* Move cursor to original position. */
|
||||
if (_pos + gbl_plen) {
|
||||
snprintf(seq, 64, "\x1b[%dC", (int)(_pos + gbl_plen));
|
||||
if (_pos + _plen) {
|
||||
snprintf(seq, 64, "\x1b[%dC", (int)(_pos + _plen));
|
||||
janet_buffer_push_cstring(&b, seq);
|
||||
}
|
||||
if (write_console((char *) b.data, b.count) == -1) {
|
||||
@@ -414,7 +432,8 @@ static int insert(char c, int draw) {
|
||||
gbl_buf[gbl_pos++] = c;
|
||||
gbl_buf[++gbl_len] = '\0';
|
||||
if (draw) {
|
||||
if (gbl_plen + gbl_len < gbl_cols) {
|
||||
int _plen = getplen();
|
||||
if (_plen + gbl_len < gbl_cols) {
|
||||
/* Avoid a full update of the line in the
|
||||
* trivial case. */
|
||||
if (write_console(&c, 1) == -1) return -1;
|
||||
@@ -925,11 +944,12 @@ 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, gbl_plen) == -1) return -1;
|
||||
if (write_console((char *) gbl_prompt, _plen) == -1) return -1;
|
||||
for (;;) {
|
||||
char c;
|
||||
char seq[5];
|
||||
|
||||
@@ -148,11 +148,10 @@
|
||||
|
||||
# os/execute with empty environment
|
||||
# pr #1686
|
||||
# native MinGW can't find system DLLs without PATH and so fails
|
||||
(assert (= (if (and (= :mingw (os/which))
|
||||
(nil? (os/stat "C:\\windows\\system32\\wineboot.exe")))
|
||||
-1073741515 0)
|
||||
(os/execute [;run janet "-e" "(+ 1 2 3)"] :pe {}))
|
||||
# native MinGW can't find system DLLs without PATH, SystemRoot, etc. and so fails
|
||||
# Also fails for address sanitizer builds on windows.
|
||||
(def result (os/execute [;run janet "-e" "(+ 1 2 3)"] :pe {}))
|
||||
(assert (or (= result -1073741515) (= result 0))
|
||||
"os/execute with minimal env")
|
||||
|
||||
# os/execute regressions
|
||||
|
||||
@@ -61,5 +61,68 @@
|
||||
(check-jdn "a string")
|
||||
(check-jdn @"a buffer")
|
||||
|
||||
# Test multiline pretty specifiers
|
||||
(let [tup [:keyword "string" @"buffer"]
|
||||
tab @{true (table/setproto @{:bar tup
|
||||
:baz 42}
|
||||
@{:_name "Foo"})}]
|
||||
(set (tab tup) tab)
|
||||
(assert (= (string/format "%m" {tup @[tup tab]
|
||||
'symbol tup})
|
||||
`
|
||||
{symbol (:keyword
|
||||
"string"
|
||||
@"buffer")
|
||||
(:keyword
|
||||
"string"
|
||||
@"buffer") @[(:keyword
|
||||
"string"
|
||||
@"buffer")
|
||||
@{true @Foo{:bar (:keyword
|
||||
"string"
|
||||
@"buffer")
|
||||
:baz 42}
|
||||
(:keyword
|
||||
"string"
|
||||
@"buffer") <cycle 2>}]}`))
|
||||
(assert (= (string/format "%p" {(freeze (zipcoll (range 42)
|
||||
(range -42 0))) tab})
|
||||
`
|
||||
{{0 -42
|
||||
1 -41
|
||||
2 -40
|
||||
3 -39
|
||||
4 -38
|
||||
5 -37
|
||||
6 -36
|
||||
7 -35
|
||||
8 -34
|
||||
9 -33
|
||||
10 -32
|
||||
11 -31
|
||||
12 -30
|
||||
13 -29
|
||||
14 -28
|
||||
15 -27
|
||||
16 -26
|
||||
17 -25
|
||||
18 -24
|
||||
19 -23
|
||||
20 -22
|
||||
21 -21
|
||||
22 -20
|
||||
23 -19
|
||||
24 -18
|
||||
25 -17
|
||||
26 -16
|
||||
27 -15
|
||||
28 -14
|
||||
29 -13
|
||||
...} @{true @Foo{:bar (:keyword
|
||||
"string"
|
||||
@"buffer")
|
||||
:baz 42}
|
||||
(:keyword
|
||||
"string"
|
||||
@"buffer") <cycle 1>}}`)))
|
||||
(end-suite)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user