mirror of
https://github.com/janet-lang/janet
synced 2026-04-28 01:31:26 +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
|
shell: cmd
|
||||||
run: build_win dist
|
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:
|
test-windows-min:
|
||||||
name: Build and test on Windows Minimal build
|
name: Build and test on Windows Minimal build
|
||||||
strategy:
|
strategy:
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
## Unreleased - ???
|
## Unreleased - ???
|
||||||
- Documentation fixes
|
- 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
|
## 1.41.2 - 2026-02-18
|
||||||
- Fix regressions in `put` for arrays and buffers.
|
- 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
|
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.
|
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
|
## C style
|
||||||
|
|
||||||
For changes to the VM and Core code, you will probably need to know C. Janet is programmed with
|
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 a good description of the problem that is being solved
|
||||||
* Include descriptions of potential solutions if you have some in mind.
|
* 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_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
|
@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=link /nologo
|
||||||
|
|
||||||
@set JANET_LINK_STATIC=lib /nologo
|
@set JANET_LINK_STATIC=lib /nologo
|
||||||
|
|||||||
@@ -443,11 +443,36 @@
|
|||||||
(def ,binding ,ctor)
|
(def ,binding ,ctor)
|
||||||
,(defer-impl :with [(or dtor :close) binding] body)))
|
,(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
|
(defmacro when-with
|
||||||
``Similar to with, but if binding is false or nil, returns
|
``Similar to with, but if binding is false or nil, returns
|
||||||
nil without evaluating the body. Otherwise, the same as `with`.``
|
nil without evaluating the body. Otherwise, the same as `with`.``
|
||||||
[[binding ctor dtor] & body]
|
[[binding ctor dtor] & body]
|
||||||
~(if-let [,binding ,ctor]
|
~(as-macro ,if-let [,binding ,ctor]
|
||||||
,(defer-impl :when-with [(or dtor :close) binding] body)))
|
,(defer-impl :when-with [(or dtor :close) binding] body)))
|
||||||
|
|
||||||
(defmacro if-with
|
(defmacro if-with
|
||||||
@@ -455,7 +480,7 @@
|
|||||||
the falsey path. Otherwise, evaluates the truthy path. In both cases,
|
the falsey path. Otherwise, evaluates the truthy path. In both cases,
|
||||||
`ctor` is bound to binding.``
|
`ctor` is bound to binding.``
|
||||||
[[binding ctor dtor] truthy &opt falsey]
|
[[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])
|
,(defer-impl :if-with [(or dtor :close) binding] [truthy])
|
||||||
,falsey))
|
,falsey))
|
||||||
|
|
||||||
@@ -539,13 +564,13 @@
|
|||||||
(case binding
|
(case binding
|
||||||
:until ~(do (if ,verb (break) nil) ,rest)
|
:until ~(do (if ,verb (break) nil) ,rest)
|
||||||
:while ~(do (if ,verb nil (break)) ,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)
|
:after ~(do ,rest ,verb nil)
|
||||||
:before ~(do ,verb ,rest nil)
|
:before ~(do ,verb ,rest nil)
|
||||||
:repeat (with-syms [iter]
|
:repeat (with-syms [iter]
|
||||||
~(do (var ,iter ,verb) (while (> ,iter 0) ,rest (-- ,iter))))
|
~(do (var ,iter ,verb) (while (,> ,iter 0) ,rest (as-macro ,-- ,iter))))
|
||||||
:when ~(when ,verb ,rest)
|
:when ~(as-macro ,when ,verb ,rest)
|
||||||
:unless ~(unless ,verb ,rest)
|
:unless ~(as-macro ,unless ,verb ,rest)
|
||||||
(error (string "unexpected loop modifier " binding))))))
|
(error (string "unexpected loop modifier " binding))))))
|
||||||
|
|
||||||
# 3 term expression
|
# 3 term expression
|
||||||
@@ -587,7 +612,7 @@
|
|||||||
"Evaluate body n times. If n is negative, body will be evaluated 0 times. Evaluates to nil."
|
"Evaluate body n times. If n is negative, body will be evaluated 0 times. Evaluates to nil."
|
||||||
[n & body]
|
[n & body]
|
||||||
(with-syms [iter]
|
(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
|
(defmacro forever
|
||||||
"Evaluate body forever in a loop, or until a break statement."
|
"Evaluate body forever in a loop, or until a break statement."
|
||||||
@@ -683,7 +708,7 @@
|
|||||||
[head & body]
|
[head & body]
|
||||||
(def $accum (gensym))
|
(def $accum (gensym))
|
||||||
(check-empty-body body)
|
(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
|
(defmacro catseq
|
||||||
``Similar to `loop`, but concatenates each element from the loop body into an array and returns that.
|
``Similar to `loop`, but concatenates each element from the loop body into an array and returns that.
|
||||||
@@ -691,21 +716,21 @@
|
|||||||
[head & body]
|
[head & body]
|
||||||
(def $accum (gensym))
|
(def $accum (gensym))
|
||||||
(check-empty-body body)
|
(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
|
(defmacro tabseq
|
||||||
``Similar to `loop`, but accumulates key value pairs into a table.
|
``Similar to `loop`, but accumulates key value pairs into a table.
|
||||||
See `loop` for details.``
|
See `loop` for details.``
|
||||||
[head key-body & value-body]
|
[head key-body & value-body]
|
||||||
(def $accum (gensym))
|
(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
|
(defmacro generate
|
||||||
``Create a generator expression using the `loop` syntax. Returns a fiber
|
``Create a generator expression using the `loop` syntax. Returns a fiber
|
||||||
that yields all values inside the loop in order. See `loop` for details.``
|
that yields all values inside the loop in order. See `loop` for details.``
|
||||||
[head & body]
|
[head & body]
|
||||||
(check-empty-body 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
|
(defmacro coro
|
||||||
"A wrapper for making fibers that may yield multiple values (coroutine). Same as `(fiber/new (fn [] ;body) :yi)`."
|
"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))
|
(each x xs (*= accum x))
|
||||||
accum)
|
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
|
(defmacro when-let
|
||||||
"Same as `(if-let bindings (do ;body))`."
|
"Same as `(if-let bindings (do ;body))`."
|
||||||
[bindings & body]
|
[bindings & body]
|
||||||
~(if-let ,bindings (do ,;body)))
|
~(as-macro ,if-let ,bindings (do ,;body)))
|
||||||
|
|
||||||
(defn comp
|
(defn comp
|
||||||
`Takes multiple functions and returns a function that is the composition
|
`Takes multiple functions and returns a function that is the composition
|
||||||
@@ -1432,7 +1432,7 @@
|
|||||||
(tuple n @[])))
|
(tuple n @[])))
|
||||||
(def sym (gensym))
|
(def sym (gensym))
|
||||||
(def parts (array/concat @[h sym] t))
|
(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))
|
(reduce fop x forms))
|
||||||
|
|
||||||
(defmacro -?>>
|
(defmacro -?>>
|
||||||
@@ -1448,7 +1448,7 @@
|
|||||||
(tuple n @[])))
|
(tuple n @[])))
|
||||||
(def sym (gensym))
|
(def sym (gensym))
|
||||||
(def parts (array/concat @[h] t @[sym]))
|
(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))
|
(reduce fop x forms))
|
||||||
|
|
||||||
(defn- walk-ind [f form]
|
(defn- walk-ind [f form]
|
||||||
@@ -2411,8 +2411,8 @@
|
|||||||
(dictionary? m) (merge-into metadata m)
|
(dictionary? m) (merge-into metadata m)
|
||||||
(error (string "invalid metadata " m))))
|
(error (string "invalid metadata " m))))
|
||||||
(with-syms [entry old-entry f]
|
(with-syms [entry old-entry f]
|
||||||
~(let [,old-entry (,dyn ',name)]
|
~(as-macro ,let [,old-entry (,dyn ',name)]
|
||||||
(def ,entry (or ,old-entry @{:ref @[nil]}))
|
(def ,entry (as-macro ,or ,old-entry @{:ref @[nil]}))
|
||||||
(,setdyn ',name ,entry)
|
(,setdyn ',name ,entry)
|
||||||
(def ,f ,fbody)
|
(def ,f ,fbody)
|
||||||
(,put-in ,entry [:ref 0] ,f)
|
(,put-in ,entry [:ref 0] ,f)
|
||||||
@@ -3953,7 +3953,7 @@
|
|||||||
``
|
``
|
||||||
[sec & body]
|
[sec & body]
|
||||||
(with-syms [f]
|
(with-syms [f]
|
||||||
~(let [,f (coro ,;body)]
|
~(as-macro ,let [,f (as-macro ,coro ,;body)]
|
||||||
(,ev/deadline ,sec nil ,f)
|
(,ev/deadline ,sec nil ,f)
|
||||||
(,resume ,f))))
|
(,resume ,f))))
|
||||||
|
|
||||||
@@ -4085,15 +4085,15 @@
|
|||||||
(defn make-ptr []
|
(defn make-ptr []
|
||||||
(assertf (ffi/lookup (if lazy (llib) lib) raw-symbol) "failed to find ffi symbol %v" raw-symbol))
|
(assertf (ffi/lookup (if lazy (llib) lib) raw-symbol) "failed to find ffi symbol %v" raw-symbol))
|
||||||
(if lazy
|
(if lazy
|
||||||
~(defn ,alias ,;meta [,;formal-args]
|
~(as-macro ,defn ,alias ,;meta [,;formal-args]
|
||||||
(,ffi/call (,(delay (make-ptr))) (,(delay (make-sig))) ,;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))))
|
(,ffi/call ,(make-ptr) ,(make-sig) ,;formal-args))))
|
||||||
|
|
||||||
(defmacro ffi/defbind :flycheck
|
(defmacro ffi/defbind :flycheck
|
||||||
"Generate bindings for native functions in a convenient manner."
|
"Generate bindings for native functions in a convenient manner."
|
||||||
[name ret-type & body]
|
[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, "structarg")) return janet_disasm_structarg(f->def);
|
||||||
if (!janet_cstrcmp(kw, "namedargs")) return janet_disasm_namedargs(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, "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, "constants")) return janet_disasm_constants(f->def);
|
||||||
if (!janet_cstrcmp(kw, "sourcemap")) return janet_disasm_sourcemap(f->def);
|
if (!janet_cstrcmp(kw, "sourcemap")) return janet_disasm_sourcemap(f->def);
|
||||||
if (!janet_cstrcmp(kw, "environments")) return janet_disasm_environments(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) {
|
JanetChannel *janet_channel_make_threaded(uint32_t limit) {
|
||||||
janet_assert(limit <= INT32_MAX, "bad limit");
|
janet_assert(limit <= INT32_MAX, "bad limit");
|
||||||
JanetChannel *channel = janet_abstract_threaded(&janet_channel_type, sizeof(JanetChannel));
|
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;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1712,20 +1712,20 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp to) {
|
|||||||
janet_free(response);
|
janet_free(response);
|
||||||
} else {
|
} else {
|
||||||
/* Normal event */
|
/* Normal event */
|
||||||
|
JanetOverlapped *jo = (JanetOverlapped *) overlapped;
|
||||||
JanetStream *stream = (JanetStream *) completionKey;
|
JanetStream *stream = (JanetStream *) completionKey;
|
||||||
JanetFiber *fiber = NULL;
|
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;
|
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;
|
fiber = stream->write_fiber;
|
||||||
}
|
}
|
||||||
if (fiber != NULL) {
|
if (fiber != NULL) {
|
||||||
fiber->flags &= ~JANET_FIBER_EV_FLAG_IN_FLIGHT;
|
fiber->flags &= ~JANET_FIBER_EV_FLAG_IN_FLIGHT;
|
||||||
/* System is done with this, we can reused this data */
|
jo->bytes_transfered = (ULONG_PTR) num_bytes_transferred;
|
||||||
overlapped->InternalHigh = (ULONG_PTR) num_bytes_transferred;
|
|
||||||
fiber->ev_callback(fiber, result ? JANET_ASYNC_EVENT_COMPLETE : JANET_ASYNC_EVENT_FAILED);
|
fiber->ev_callback(fiber, result ? JANET_ASYNC_EVENT_COMPLETE : JANET_ASYNC_EVENT_FAILED);
|
||||||
} else {
|
} else {
|
||||||
janet_free((void *) overlapped);
|
janet_free((void *) jo);
|
||||||
janet_ev_dec_refcount();
|
janet_ev_dec_refcount();
|
||||||
}
|
}
|
||||||
janet_stream_checktoclose(stream);
|
janet_stream_checktoclose(stream);
|
||||||
@@ -2437,7 +2437,7 @@ typedef enum {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
OVERLAPPED overlapped;
|
JanetOverlapped overlapped;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
#ifdef JANET_NET
|
#ifdef JANET_NET
|
||||||
WSABUF wbuf;
|
WSABUF wbuf;
|
||||||
@@ -2472,7 +2472,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
case JANET_ASYNC_EVENT_FAILED:
|
case JANET_ASYNC_EVENT_FAILED:
|
||||||
case JANET_ASYNC_EVENT_COMPLETE: {
|
case JANET_ASYNC_EVENT_COMPLETE: {
|
||||||
/* Called when read finished */
|
/* 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;
|
state->bytes_read += ev_bytes;
|
||||||
if (state->bytes_read == 0 && (state->mode != JANET_ASYNC_READMODE_RECVFROM)) {
|
if (state->bytes_read == 0 && (state->mode != JANET_ASYNC_READMODE_RECVFROM)) {
|
||||||
janet_schedule(fiber, janet_wrap_nil());
|
janet_schedule(fiber, janet_wrap_nil());
|
||||||
@@ -2504,7 +2504,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
case JANET_ASYNC_EVENT_INIT: {
|
case JANET_ASYNC_EVENT_INIT: {
|
||||||
int32_t chunk_size = state->bytes_left > JANET_EV_CHUNKSIZE ? JANET_EV_CHUNKSIZE : state->bytes_left;
|
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;
|
int status;
|
||||||
#ifdef JANET_NET
|
#ifdef JANET_NET
|
||||||
if (state->mode == JANET_ASYNC_READMODE_RECVFROM) {
|
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->wbuf.buf = (char *) state->chunk_buf;
|
||||||
state->fromlen = sizeof(state->from);
|
state->fromlen = sizeof(state->from);
|
||||||
status = WSARecvFrom((SOCKET) stream->handle, &state->wbuf, 1,
|
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())) {
|
if (status && (WSA_IO_PENDING != WSAGetLastError())) {
|
||||||
janet_cancel(fiber, janet_ev_lasterr());
|
janet_cancel(fiber, janet_ev_lasterr());
|
||||||
janet_async_end(fiber);
|
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
|
/* 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 */
|
* 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 (!status && (ERROR_IO_PENDING != GetLastError())) {
|
||||||
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
||||||
if (state->bytes_read) {
|
if (state->bytes_read) {
|
||||||
@@ -2681,7 +2681,7 @@ typedef enum {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
OVERLAPPED overlapped;
|
JanetOverlapped overlapped;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
#ifdef JANET_NET
|
#ifdef JANET_NET
|
||||||
WSABUF wbuf;
|
WSABUF wbuf;
|
||||||
@@ -2722,7 +2722,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
case JANET_ASYNC_EVENT_FAILED:
|
case JANET_ASYNC_EVENT_FAILED:
|
||||||
case JANET_ASYNC_EVENT_COMPLETE: {
|
case JANET_ASYNC_EVENT_COMPLETE: {
|
||||||
/* Called when write finished */
|
/* 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)) {
|
if (ev_bytes == 0 && (state->mode != JANET_ASYNC_WRITEMODE_SENDTO)) {
|
||||||
janet_cancel(fiber, janet_cstringv("disconnect"));
|
janet_cancel(fiber, janet_cstringv("disconnect"));
|
||||||
janet_async_end(fiber);
|
janet_async_end(fiber);
|
||||||
@@ -2751,7 +2751,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
bytes = state->src.str;
|
bytes = state->src.str;
|
||||||
len = janet_string_length(bytes);
|
len = janet_string_length(bytes);
|
||||||
}
|
}
|
||||||
memset(&(state->overlapped), 0, sizeof(WSAOVERLAPPED));
|
memset(&(state->overlapped), 0, sizeof(JanetOverlapped));
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
#ifdef JANET_NET
|
#ifdef JANET_NET
|
||||||
@@ -2761,7 +2761,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
state->wbuf.len = len;
|
state->wbuf.len = len;
|
||||||
const struct sockaddr *to = state->dest_abst;
|
const struct sockaddr *to = state->dest_abst;
|
||||||
int tolen = (int) janet_abstract_size((void *) to);
|
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 (status) {
|
||||||
if (WSA_IO_PENDING == WSAGetLastError()) {
|
if (WSA_IO_PENDING == WSAGetLastError()) {
|
||||||
janet_async_in_flight(fiber);
|
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
|
* for more details see the lpOverlapped parameter in
|
||||||
* https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
|
* https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
|
||||||
*/
|
*/
|
||||||
state->overlapped.Offset = (DWORD) 0xFFFFFFFF;
|
state->overlapped.as.overlapped.Offset = (DWORD) 0xFFFFFFFF;
|
||||||
state->overlapped.OffsetHigh = (DWORD) 0xFFFFFFFF;
|
state->overlapped.as.overlapped.OffsetHigh = (DWORD) 0xFFFFFFFF;
|
||||||
status = WriteFile(stream->handle, bytes, len, NULL, &state->overlapped);
|
status = WriteFile(stream->handle, bytes, len, NULL, &state->overlapped.as.overlapped);
|
||||||
if (!status) {
|
if (!status) {
|
||||||
if (ERROR_IO_PENDING == GetLastError()) {
|
if (ERROR_IO_PENDING == GetLastError()) {
|
||||||
janet_async_in_flight(fiber);
|
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)
|
#define FILE_INFO_PADDING (4096 * 4)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
OVERLAPPED overlapped;
|
JanetOverlapped overlapped;
|
||||||
JanetStream *stream;
|
JanetStream *stream;
|
||||||
JanetWatcher *watcher;
|
JanetWatcher *watcher;
|
||||||
JanetFiber *fiber;
|
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);
|
Janet pathv = janet_wrap_string(ow->dir_path);
|
||||||
ow->flags = flags | watcher->default_flags;
|
ow->flags = flags | watcher->default_flags;
|
||||||
ow->watcher = watcher;
|
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 streamv = janet_wrap_pointer(ow);
|
||||||
janet_table_put(watcher->watch_descriptors, pathv, streamv);
|
janet_table_put(watcher->watch_descriptors, pathv, streamv);
|
||||||
if (watcher->is_watching) {
|
if (watcher->is_watching) {
|
||||||
|
|||||||
@@ -140,6 +140,35 @@ static int net_get_address_family(Janet x) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* State machine for async connect */
|
/* 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) {
|
void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||||
JanetStream *stream = fiber->ev_stream;
|
JanetStream *stream = fiber->ev_stream;
|
||||||
@@ -159,15 +188,21 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
|
/* We should be using ConnectEx here */
|
||||||
int res = 0;
|
int res = 0;
|
||||||
int size = sizeof(res);
|
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
|
#else
|
||||||
int res = 0;
|
int res = 0;
|
||||||
socklen_t size = sizeof res;
|
socklen_t size = sizeof(res);
|
||||||
int r = getsockopt(stream->handle, SOL_SOCKET, SO_ERROR, &res, &size);
|
int r = getsockopt(stream->handle, SOL_SOCKET, SO_ERROR, &res, &size);
|
||||||
|
const int no_error = 0;
|
||||||
#endif
|
#endif
|
||||||
if (r == 0) {
|
if (r == no_error) {
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
janet_schedule(fiber, janet_wrap_abstract(stream));
|
janet_schedule(fiber, janet_wrap_abstract(stream));
|
||||||
} else {
|
} else {
|
||||||
@@ -181,8 +216,8 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
janet_async_end(fiber);
|
janet_async_end(fiber);
|
||||||
}
|
}
|
||||||
|
|
||||||
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
|
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream, void *state) {
|
||||||
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, NULL);
|
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* State machine for accepting connections. */
|
/* State machine for accepting connections. */
|
||||||
@@ -190,7 +225,7 @@ static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
|
|||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
WSAOVERLAPPED overlapped;
|
JanetOverlapped overlapped;
|
||||||
JanetFunction *function;
|
JanetFunction *function;
|
||||||
JanetStream *lstream;
|
JanetStream *lstream;
|
||||||
JanetStream *astream;
|
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_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
|
||||||
Janet err;
|
Janet err;
|
||||||
NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
|
NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
|
||||||
memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED));
|
memset(&state->overlapped, 0, sizeof(JanetOverlapped));
|
||||||
memset(&state->buf, 0, 1024);
|
memset(&state->buf, 0, 1024);
|
||||||
state->function = fun;
|
state->function = fun;
|
||||||
state->lstream = stream;
|
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);
|
JanetStream *astream = make_stream(asock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
|
||||||
state->astream = astream;
|
state->astream = astream;
|
||||||
int socksize = sizeof(SOCKADDR_STORAGE) + 16;
|
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();
|
int code = WSAGetLastError();
|
||||||
if (code == WSA_IO_PENDING) {
|
if (code == WSA_IO_PENDING) {
|
||||||
/* indicates io is happening async */
|
/* indicates io is happening async */
|
||||||
@@ -572,11 +607,39 @@ JANET_CORE_FN(cfun_net_connect,
|
|||||||
|
|
||||||
/* Connect to socket */
|
/* Connect to socket */
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
|
int status = 0;
|
||||||
int err = WSAGetLastError();
|
int err = 0;
|
||||||
freeaddrinfo(ai);
|
LPFN_CONNECTEX connect_ex = NULL;
|
||||||
/* Set up the socket for non-blocking IO after connecting on windows by default */
|
if (socktype == SOCK_STREAM && ((connect_ex = lazy_get_connectex(sock)))) {
|
||||||
janet_net_socknoblock(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
|
#else
|
||||||
/* Set up the socket for non-blocking IO before connecting */
|
/* Set up the socket for non-blocking IO before connecting */
|
||||||
janet_net_socknoblock(sock);
|
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,
|
JANET_CORE_FN(cfun_net_socket,
|
||||||
@@ -1213,6 +1276,8 @@ void janet_net_init(void) {
|
|||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
WSADATA wsaData;
|
WSADATA wsaData;
|
||||||
janet_assert(!WSAStartup(MAKEWORD(2, 2), &wsaData), "could not start winsock");
|
janet_assert(!WSAStartup(MAKEWORD(2, 2), &wsaData), "could not start winsock");
|
||||||
|
janet_vm.connect_ex_loaded = 0;
|
||||||
|
janet_vm.connect_ex = NULL;
|
||||||
#endif
|
#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);
|
janet_buffer_extra(buffer, BUFSIZE);
|
||||||
uint8_t *buf = buffer->data + buffer->count;
|
uint8_t *buf = buffer->data + buffer->count;
|
||||||
int32_t neg = 0;
|
int32_t neg = 0;
|
||||||
@@ -80,7 +80,7 @@ static void integer_to_string_b(JanetBuffer *buffer, int32_t x) {
|
|||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
buf[0] = '0';
|
buf[0] = '0';
|
||||||
buffer->count++;
|
buffer->count++;
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
if (x > 0) {
|
if (x > 0) {
|
||||||
x = -x;
|
x = -x;
|
||||||
@@ -96,6 +96,7 @@ static void integer_to_string_b(JanetBuffer *buffer, int32_t x) {
|
|||||||
x /= 10;
|
x /= 10;
|
||||||
}
|
}
|
||||||
buffer->count += len + neg;
|
buffer->count += len + neg;
|
||||||
|
return len + neg;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HEX(i) (((uint8_t *) janet_base64)[(i)])
|
#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
|
#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, '"');
|
janet_buffer_push_u8(buffer, '"');
|
||||||
|
int align = 1;
|
||||||
for (int32_t i = 0; i < len; ++i) {
|
for (int32_t i = 0; i < len; ++i) {
|
||||||
uint8_t c = str[i];
|
uint8_t c = str[i];
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '"':
|
case '"':
|
||||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\"", 2);
|
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\"", 2);
|
||||||
|
align += 2;
|
||||||
break;
|
break;
|
||||||
case '\n':
|
case '\n':
|
||||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\n", 2);
|
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\n", 2);
|
||||||
|
align += 2;
|
||||||
break;
|
break;
|
||||||
case '\r':
|
case '\r':
|
||||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\r", 2);
|
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\r", 2);
|
||||||
|
align += 2;
|
||||||
break;
|
break;
|
||||||
case '\0':
|
case '\0':
|
||||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\0", 2);
|
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\0", 2);
|
||||||
|
align += 2;
|
||||||
break;
|
break;
|
||||||
case '\f':
|
case '\f':
|
||||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\f", 2);
|
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\f", 2);
|
||||||
|
align += 2;
|
||||||
break;
|
break;
|
||||||
case '\v':
|
case '\v':
|
||||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\v", 2);
|
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\v", 2);
|
||||||
|
align += 2;
|
||||||
break;
|
break;
|
||||||
case '\a':
|
case '\a':
|
||||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\a", 2);
|
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\a", 2);
|
||||||
|
align += 2;
|
||||||
break;
|
break;
|
||||||
case '\b':
|
case '\b':
|
||||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\b", 2);
|
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\b", 2);
|
||||||
|
align += 2;
|
||||||
break;
|
break;
|
||||||
case 27:
|
case 27:
|
||||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\e", 2);
|
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\e", 2);
|
||||||
|
align += 2;
|
||||||
break;
|
break;
|
||||||
case '\\':
|
case '\\':
|
||||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\\", 2);
|
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\\", 2);
|
||||||
|
align += 2;
|
||||||
break;
|
break;
|
||||||
case '\t':
|
case '\t':
|
||||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\t", 2);
|
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\t", 2);
|
||||||
|
align += 2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (c < 32 || c > 126) {
|
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[2] = janet_base64[(c >> 4) & 0xF];
|
||||||
buf[3] = janet_base64[c & 0xF];
|
buf[3] = janet_base64[c & 0xF];
|
||||||
janet_buffer_push_bytes(buffer, buf, 4);
|
janet_buffer_push_bytes(buffer, buf, 4);
|
||||||
|
align += 4;
|
||||||
} else {
|
} else {
|
||||||
janet_buffer_push_u8(buffer, c);
|
janet_buffer_push_u8(buffer, c);
|
||||||
|
align++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
janet_buffer_push_u8(buffer, '"');
|
janet_buffer_push_u8(buffer, '"');
|
||||||
|
return align + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void janet_escape_string_b(JanetBuffer *buffer, const uint8_t *str) {
|
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 {
|
struct pretty {
|
||||||
JanetBuffer *buffer;
|
JanetBuffer *buffer;
|
||||||
int depth;
|
int depth;
|
||||||
int indent;
|
int align;
|
||||||
int flags;
|
int flags;
|
||||||
int32_t bufstartlen;
|
int32_t bufstartlen;
|
||||||
int32_t *keysort_buffer;
|
int32_t *keysort_buffer;
|
||||||
@@ -450,14 +466,15 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_newline(struct pretty *S, int just_a_space) {
|
static void print_newline(struct pretty *S, int align) {
|
||||||
int i;
|
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, ' ');
|
janet_buffer_push_u8(S->buffer, ' ');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
janet_buffer_push_u8(S->buffer, '\n');
|
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, ' ');
|
janet_buffer_push_u8(S->buffer, ' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,14 +501,12 @@ static const char *janet_pretty_colors[] = {
|
|||||||
"\x1B[36m"
|
"\x1B[36m"
|
||||||
};
|
};
|
||||||
|
|
||||||
#define JANET_PRETTY_DICT_ONELINE 4
|
|
||||||
#define JANET_PRETTY_IND_ONELINE 10
|
|
||||||
#define JANET_PRETTY_DICT_LIMIT 30
|
#define JANET_PRETTY_DICT_LIMIT 30
|
||||||
#define JANET_PRETTY_DICT_KEYSORT_LIMIT 2000
|
#define JANET_PRETTY_DICT_KEYSORT_LIMIT 2000
|
||||||
#define JANET_PRETTY_ARRAY_LIMIT 160
|
#define JANET_PRETTY_ARRAY_LIMIT 160
|
||||||
|
|
||||||
/* Helper for pretty printing */
|
/* 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 */
|
/* Add to seen */
|
||||||
switch (janet_type(x)) {
|
switch (janet_type(x)) {
|
||||||
case JANET_NIL:
|
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, janet_cycle_color);
|
||||||
}
|
}
|
||||||
janet_buffer_push_cstring(S->buffer, "<cycle ");
|
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, '>');
|
janet_buffer_push_u8(S->buffer, '>');
|
||||||
if (S->flags & JANET_PRETTY_COLOR) {
|
if (S->flags & JANET_PRETTY_COLOR) {
|
||||||
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
|
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) {
|
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_ensure(S->buffer, S->buffer->count + S->bufstartlen * 4 + 3, 1);
|
||||||
janet_buffer_push_u8(S->buffer, '@');
|
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 {
|
} else {
|
||||||
|
S->align -= S->buffer->count;
|
||||||
janet_description_b(S->buffer, x);
|
janet_description_b(S->buffer, x);
|
||||||
|
S->align += S->buffer->count;
|
||||||
}
|
}
|
||||||
if (color && (S->flags & JANET_PRETTY_COLOR)) {
|
if (color && (S->flags & JANET_PRETTY_COLOR)) {
|
||||||
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
|
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 *startstr = isarray ? "@[" : hasbrackets ? "[" : "(";
|
||||||
const char endchar = isarray ? ']' : hasbrackets ? ']' : ')';
|
const char endchar = isarray ? ']' : hasbrackets ? ']' : ')';
|
||||||
janet_buffer_push_cstring(S->buffer, startstr);
|
janet_buffer_push_cstring(S->buffer, startstr);
|
||||||
|
const int align = S->align += strlen(startstr);
|
||||||
S->depth--;
|
S->depth--;
|
||||||
S->indent += 2;
|
|
||||||
if (S->depth == 0) {
|
if (S->depth == 0) {
|
||||||
janet_buffer_push_cstring(S->buffer, "...");
|
janet_buffer_push_cstring(S->buffer, "...");
|
||||||
|
S->align += 3;
|
||||||
} else {
|
} 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)) {
|
if (len > JANET_PRETTY_ARRAY_LIMIT && !(S->flags & JANET_PRETTY_NOTRUNC)) {
|
||||||
for (i = 0; i < 3; i++) {
|
for (i = 0; i < 3; i++) {
|
||||||
if (i) print_newline(S, 0);
|
if (i) print_newline(S, align);
|
||||||
janet_pretty_one(S, arr[i], 0);
|
janet_pretty_one(S, arr[i]);
|
||||||
}
|
}
|
||||||
print_newline(S, 0);
|
print_newline(S, align);
|
||||||
janet_buffer_push_cstring(S->buffer, "...");
|
janet_buffer_push_cstring(S->buffer, "...");
|
||||||
for (i = 0; i < 3; i++) {
|
S->align += 3;
|
||||||
print_newline(S, 0);
|
for (i = len - 3; i < len; i++) {
|
||||||
janet_pretty_one(S, arr[len - 3 + i], 0);
|
print_newline(S, align);
|
||||||
|
janet_pretty_one(S, arr[i]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
if (i) print_newline(S, len < JANET_PRETTY_IND_ONELINE);
|
if (i) print_newline(S, align);
|
||||||
janet_pretty_one(S, arr[i], 0);
|
janet_pretty_one(S, arr[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
S->indent -= 2;
|
|
||||||
S->depth++;
|
S->depth++;
|
||||||
janet_buffer_push_u8(S->buffer, endchar);
|
janet_buffer_push_u8(S->buffer, endchar);
|
||||||
|
S->align++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case JANET_STRUCT:
|
case JANET_STRUCT:
|
||||||
@@ -586,6 +602,7 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
|||||||
if (istable) {
|
if (istable) {
|
||||||
JanetTable *t = janet_unwrap_table(x);
|
JanetTable *t = janet_unwrap_table(x);
|
||||||
JanetTable *proto = t->proto;
|
JanetTable *proto = t->proto;
|
||||||
|
S->align++;
|
||||||
janet_buffer_push_cstring(S->buffer, "@");
|
janet_buffer_push_cstring(S->buffer, "@");
|
||||||
if (NULL != proto) {
|
if (NULL != proto) {
|
||||||
Janet name = janet_table_get(proto, janet_ckeywordv("_name"));
|
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_cstring(S->buffer, janet_class_color);
|
||||||
}
|
}
|
||||||
janet_buffer_push_bytes(S->buffer, n, len);
|
janet_buffer_push_bytes(S->buffer, n, len);
|
||||||
|
S->align += len;
|
||||||
if (S->flags & JANET_PRETTY_COLOR) {
|
if (S->flags & JANET_PRETTY_COLOR) {
|
||||||
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
|
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_cstring(S->buffer, janet_class_color);
|
||||||
}
|
}
|
||||||
janet_buffer_push_bytes(S->buffer, n, len);
|
janet_buffer_push_bytes(S->buffer, n, len);
|
||||||
|
S->align += len;
|
||||||
if (S->flags & JANET_PRETTY_COLOR) {
|
if (S->flags & JANET_PRETTY_COLOR) {
|
||||||
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
|
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->depth--;
|
||||||
S->indent += 2;
|
|
||||||
if (S->depth == 0) {
|
if (S->depth == 0) {
|
||||||
janet_buffer_push_cstring(S->buffer, "...");
|
janet_buffer_push_cstring(S->buffer, "...");
|
||||||
|
S->align += 3;
|
||||||
} else {
|
} else {
|
||||||
int32_t len = 0, cap = 0;
|
int32_t len = 0, cap = 0;
|
||||||
const JanetKV *kvs = NULL;
|
const JanetKV *kvs = NULL;
|
||||||
janet_dictionary_view(x, &kvs, &len, &cap);
|
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;
|
int32_t ks_start = S->keysort_start;
|
||||||
int truncated = 0;
|
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;
|
int32_t j = 0;
|
||||||
for (int32_t i = 0; i < len; i++) {
|
for (int32_t i = 0; i < len; i++) {
|
||||||
while (janet_checktype(kvs[j].key, JANET_NIL)) j++;
|
while (janet_checktype(kvs[j].key, JANET_NIL)) j++;
|
||||||
if (i) print_newline(S, len < JANET_PRETTY_DICT_ONELINE);
|
if (i) print_newline(S, align);
|
||||||
janet_pretty_one(S, kvs[j].key, 0);
|
janet_pretty_one(S, kvs[j].key);
|
||||||
janet_buffer_push_u8(S->buffer, ' ');
|
janet_buffer_push_u8(S->buffer, ' ');
|
||||||
janet_pretty_one(S, kvs[j].value, 1);
|
S->align++;
|
||||||
|
janet_pretty_one(S, kvs[j].value);
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
if (truncated) {
|
if (truncated) {
|
||||||
print_newline(S, 0);
|
print_newline(S, align);
|
||||||
janet_buffer_push_cstring(S->buffer, "...");
|
janet_buffer_push_cstring(S->buffer, "...");
|
||||||
|
S->align += 3;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Sorted keys dictionaries */
|
/* 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++) {
|
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];
|
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_buffer_push_u8(S->buffer, ' ');
|
||||||
janet_pretty_one(S, kvs[j].value, 1);
|
S->align++;
|
||||||
|
janet_pretty_one(S, kvs[j].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (truncated) {
|
if (truncated) {
|
||||||
print_newline(S, 0);
|
print_newline(S, align);
|
||||||
janet_buffer_push_cstring(S->buffer, "...");
|
janet_buffer_push_cstring(S->buffer, "...");
|
||||||
|
S->align += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
S->keysort_start = ks_start;
|
S->keysort_start = ks_start;
|
||||||
}
|
}
|
||||||
S->indent -= 2;
|
|
||||||
S->depth++;
|
S->depth++;
|
||||||
janet_buffer_push_u8(S->buffer, '}');
|
janet_buffer_push_u8(S->buffer, '}');
|
||||||
|
S->align++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -718,14 +739,14 @@ static JanetBuffer *janet_pretty_(JanetBuffer *buffer, int depth, int flags, Jan
|
|||||||
}
|
}
|
||||||
S.buffer = buffer;
|
S.buffer = buffer;
|
||||||
S.depth = depth;
|
S.depth = depth;
|
||||||
S.indent = 0;
|
S.align = 0;
|
||||||
S.flags = flags;
|
S.flags = flags;
|
||||||
S.bufstartlen = startlen;
|
S.bufstartlen = startlen;
|
||||||
S.keysort_capacity = 0;
|
S.keysort_capacity = 0;
|
||||||
S.keysort_buffer = NULL;
|
S.keysort_buffer = NULL;
|
||||||
S.keysort_start = 0;
|
S.keysort_start = 0;
|
||||||
janet_table_init(&S.seen, 10);
|
janet_table_init(&S.seen, 10);
|
||||||
janet_pretty_one(&S, x, 0);
|
janet_pretty_one(&S, x);
|
||||||
janet_table_deinit(&S.seen);
|
janet_table_deinit(&S.seen);
|
||||||
return S.buffer;
|
return S.buffer;
|
||||||
}
|
}
|
||||||
@@ -743,7 +764,7 @@ static JanetBuffer *janet_jdn_(JanetBuffer *buffer, int depth, Janet x, int32_t
|
|||||||
}
|
}
|
||||||
S.buffer = buffer;
|
S.buffer = buffer;
|
||||||
S.depth = depth;
|
S.depth = depth;
|
||||||
S.indent = 0;
|
S.align = 0;
|
||||||
S.flags = 0;
|
S.flags = 0;
|
||||||
S.bufstartlen = startlen;
|
S.bufstartlen = startlen;
|
||||||
S.keysort_capacity = 0;
|
S.keysort_capacity = 0;
|
||||||
|
|||||||
@@ -182,6 +182,8 @@ struct JanetVM {
|
|||||||
JanetTable signal_handlers;
|
JanetTable signal_handlers;
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
void **iocp;
|
void **iocp;
|
||||||
|
void *connect_ex; /* MSWsock extension if available */
|
||||||
|
int connect_ex_loaded;
|
||||||
#elif defined(JANET_EV_EPOLL)
|
#elif defined(JANET_EV_EPOLL)
|
||||||
pthread_attr_t new_thread_attr;
|
pthread_attr_t new_thread_attr;
|
||||||
JanetHandle selfpipe[2];
|
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)
|
#define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR)
|
||||||
#endif
|
#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 */
|
/* Initialize builtin libraries */
|
||||||
void janet_lib_io(JanetTable *env);
|
void janet_lib_io(JanetTable *env);
|
||||||
void janet_lib_math(JanetTable *env);
|
void janet_lib_math(JanetTable *env);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include <janet.h>
|
#include <janet.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#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) {
|
static void refresh(void) {
|
||||||
char seq[64];
|
char seq[64];
|
||||||
JanetBuffer b;
|
JanetBuffer b;
|
||||||
|
|
||||||
|
/* If prompt is too long, truncate */
|
||||||
|
int _plen = getplen();
|
||||||
|
|
||||||
/* Keep cursor position on screen */
|
/* Keep cursor position on screen */
|
||||||
char *_buf = gbl_buf;
|
char *_buf = gbl_buf;
|
||||||
int _len = gbl_len;
|
int _len = gbl_len;
|
||||||
int _pos = gbl_pos;
|
int _pos = gbl_pos;
|
||||||
while ((gbl_plen + _pos) >= gbl_cols) {
|
|
||||||
|
while ((_plen + _pos) >= gbl_cols) {
|
||||||
_buf++;
|
_buf++;
|
||||||
_len--;
|
_len--;
|
||||||
_pos--;
|
_pos--;
|
||||||
}
|
}
|
||||||
while ((gbl_plen + _len) > gbl_cols) {
|
|
||||||
|
while ((_plen + _len) > gbl_cols) {
|
||||||
_len--;
|
_len--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
janet_buffer_init(&b, 0);
|
janet_buffer_init(&b, 0);
|
||||||
/* Cursor to left edge, gbl_prompt and buffer */
|
/* Cursor to left edge, gbl_prompt and buffer */
|
||||||
janet_buffer_push_u8(&b, '\r');
|
janet_buffer_push_u8(&b, '\r');
|
||||||
janet_buffer_push_cstring(&b, gbl_prompt);
|
janet_buffer_push_bytes(&b, (const uint8_t *) gbl_prompt, _plen);
|
||||||
janet_buffer_push_bytes(&b, (uint8_t *) _buf, _len);
|
if (_len > 0) {
|
||||||
|
janet_buffer_push_bytes(&b, (uint8_t *) _buf, _len);
|
||||||
|
}
|
||||||
/* Erase to right */
|
/* Erase to right */
|
||||||
janet_buffer_push_cstring(&b, "\x1b[0K\r");
|
janet_buffer_push_cstring(&b, "\x1b[0K\r");
|
||||||
/* Move cursor to original position. */
|
/* Move cursor to original position. */
|
||||||
if (_pos + gbl_plen) {
|
if (_pos + _plen) {
|
||||||
snprintf(seq, 64, "\x1b[%dC", (int)(_pos + gbl_plen));
|
snprintf(seq, 64, "\x1b[%dC", (int)(_pos + _plen));
|
||||||
janet_buffer_push_cstring(&b, seq);
|
janet_buffer_push_cstring(&b, seq);
|
||||||
}
|
}
|
||||||
if (write_console((char *) b.data, b.count) == -1) {
|
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_pos++] = c;
|
||||||
gbl_buf[++gbl_len] = '\0';
|
gbl_buf[++gbl_len] = '\0';
|
||||||
if (draw) {
|
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
|
/* Avoid a full update of the line in the
|
||||||
* trivial case. */
|
* trivial case. */
|
||||||
if (write_console(&c, 1) == -1) return -1;
|
if (write_console(&c, 1) == -1) return -1;
|
||||||
@@ -925,11 +944,12 @@ static int line() {
|
|||||||
gbl_len = 0;
|
gbl_len = 0;
|
||||||
gbl_pos = 0;
|
gbl_pos = 0;
|
||||||
while (gbl_prompt[gbl_plen]) gbl_plen++;
|
while (gbl_prompt[gbl_plen]) gbl_plen++;
|
||||||
|
int _plen = getplen();
|
||||||
gbl_buf[0] = '\0';
|
gbl_buf[0] = '\0';
|
||||||
|
|
||||||
addhistory();
|
addhistory();
|
||||||
|
|
||||||
if (write_console((char *) gbl_prompt, gbl_plen) == -1) return -1;
|
if (write_console((char *) gbl_prompt, _plen) == -1) return -1;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
char c;
|
char c;
|
||||||
char seq[5];
|
char seq[5];
|
||||||
|
|||||||
@@ -148,11 +148,10 @@
|
|||||||
|
|
||||||
# os/execute with empty environment
|
# os/execute with empty environment
|
||||||
# pr #1686
|
# pr #1686
|
||||||
# native MinGW can't find system DLLs without PATH and so fails
|
# native MinGW can't find system DLLs without PATH, SystemRoot, etc. and so fails
|
||||||
(assert (= (if (and (= :mingw (os/which))
|
# Also fails for address sanitizer builds on windows.
|
||||||
(nil? (os/stat "C:\\windows\\system32\\wineboot.exe")))
|
(def result (os/execute [;run janet "-e" "(+ 1 2 3)"] :pe {}))
|
||||||
-1073741515 0)
|
(assert (or (= result -1073741515) (= result 0))
|
||||||
(os/execute [;run janet "-e" "(+ 1 2 3)"] :pe {}))
|
|
||||||
"os/execute with minimal env")
|
"os/execute with minimal env")
|
||||||
|
|
||||||
# os/execute regressions
|
# os/execute regressions
|
||||||
|
|||||||
@@ -61,5 +61,68 @@
|
|||||||
(check-jdn "a string")
|
(check-jdn "a string")
|
||||||
(check-jdn @"a buffer")
|
(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)
|
(end-suite)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user