1
0
mirror of https://github.com/janet-lang/janet synced 2025-11-18 00:05:13 +00:00

Compare commits

..

47 Commits

Author SHA1 Message Date
Calvin Rose
7e0c692d4e Remove test-install target from sr.ht 2025-11-15 08:39:47 -06:00
Calvin Rose
732fe0ad03 Try new openbsd. 2025-11-15 08:33:41 -06:00
Calvin Rose
0c8622c803 posix_chroot warning 2025-11-15 08:15:26 -06:00
Calvin Rose
94f2494f8d MSVC warning 2025-11-15 08:14:28 -06:00
Calvin Rose
0f9ecc2da5 Prepare for 1.40.0 release 2025-11-15 07:31:23 -06:00
Calvin Rose
83f5da3b8f Merge pull request #1662 from McSinyx/dobytes-err-ctx
Include context in dobytes' error string
2025-11-09 09:20:23 -06:00
Calvin Rose
9b9f2a1713 Merge pull request #1660 from pyrmont/bugfix.confirm-bundle-files
Clarify error message for missing bundle script during bundle installation
2025-11-09 09:06:06 -06:00
Calvin Rose
8df4d47ede Merge pull request #1663 from McSinyx/dobytes-err-keep
Keep dobytes' error string
2025-11-09 08:40:07 -06:00
Nguyễn Gia Phong
1c372fbf32 Keep dobytes' error string
The reason for failure would be more useful
than the most recently evaluated value.
2025-11-05 17:00:04 +09:00
Nguyễn Gia Phong
8ace580498 Include context in dobytes' error string 2025-11-05 16:53:19 +09:00
Michael Camilleri
8241d9cbb4 Revert support for bundles without an info file 2025-10-27 09:32:38 +09:00
Michael Camilleri
6bd02bb5b6 Confirm necessary files during bundle installation 2025-10-26 06:53:27 +09:00
Calvin Rose
2a3308005d Merge pull request #1659 from ianthehenry/try-macro-hygiene
fix (try) macro hygiene
2025-10-25 12:15:12 -05:00
Ian Henry
0c34033b72 add some tests for the (try) macro 2025-10-24 21:56:07 -07:00
Ian Henry
f1ec0cc48b fix try macro hygiene
Allow re-using the same symbol for the fiber and error. This allows you to
write code like (try (print "hi") ([_ _] (print "oh no"))), fixing a regression
introduced in #1605.
2025-10-24 21:33:15 -07:00
Calvin Rose
98265f0637 Merge pull request #1658 from sogaiu/tweak-has-bin-script 2025-10-17 15:44:50 -05:00
sogaiu
1018cb9cca Support :has-exe as well
This is a companion change to spork's issue

As implemented, :has-been-script didn't quite
mean what was intended.  :has-exe should
be an improvement.

To prevent breakage, both :has-bin-script and
:has-exe should be supported for a while in
both spork and janet.  Eventually
:has-bin-script can be retired.
2025-10-17 22:36:45 +09:00
Calvin Rose
2204209133 Merge pull request #1656 from jpaquim/build-system-consistency
Minor adjustments to Makefile
2025-10-15 07:51:12 -05:00
jpaquim
95abff31d9 Fix file mention in CONTRIBUTING.md 2025-10-11 17:48:23 +01:00
jpaquim
a776466423 Add $(RUN) for valgrind/callgrind, add dedicated callgrind-test target 2025-10-11 17:45:44 +01:00
jpaquim
511c1f4b0a Refactor with image-only variable 2025-10-11 16:32:24 +01:00
jpaquim
c29195596e Adapt generated file comments to build type 2025-10-11 16:29:14 +01:00
jpaquim
56027227fb Add missing phony targets and documentation for install-{jpm,spork}-git 2025-10-11 16:11:05 +01:00
jpaquim
c057e14b20 Fix dependencies and variable names in Makefile 2025-10-11 16:02:02 +01:00
Calvin Rose
db7f741dad Extend environment variable behavior to NO_COLOR and JANET_PROFILE
Env vars set to empty strings should behave the same as unset variables.
2025-10-04 13:12:29 -05:00
Calvin Rose
c901edbfb9 Interpret an empty JANET_PATH as unset
This is a common idiom with environment variables, where
a variable set to the empty string should behave the same as
an unset variable.
2025-10-04 10:43:55 -05:00
Calvin Rose
8fd1672963 Merge pull request #1652 from sogaiu/fix-bundle-install-breakage 2025-09-29 09:53:47 -05:00
sogaiu
9b99fc44b9 Revert some bundle/install changes 2025-09-29 23:45:53 +09:00
Michael Camilleri
f393531335 Add bundle/add-manpage 2025-09-28 19:27:53 -05:00
Michael Camilleri
6b8e5249ca Change :dependencies hook to :postdeps 2025-09-28 19:27:53 -05:00
Michael Camilleri
6a96b615f0 Tidy up bundle/ docstrings 2025-09-28 19:27:53 -05:00
Calvin Rose
8ec465d308 Merge pull request #1651 from neuschaefer/a64be
Recognise aarch64_be as big-endian
2025-09-27 14:29:47 -05:00
J. Neuschäfer
07bfd34c2f Recognise aarch64_be as big-endian 2025-09-26 02:07:09 +00:00
Calvin Rose
5f7878c00f Merge pull request #1649 from jsks/kvs-docstring 2025-09-23 06:42:51 -05:00
Joshua Krusell
aaf8ac2217 Tweak kvs docstring 2025-09-23 11:28:06 +02:00
Calvin Rose
73b1cf547e Update CHANGELOG 2025-09-21 10:26:49 -05:00
Calvin Rose
ed2ae562c6 Merge pull request #1647 from sogaiu/tweak-flycheck-related-docstrings 2025-09-21 00:53:51 -05:00
sogaiu
dd59d84b51 Tweak some flycheck-related docstrings 2025-09-21 14:09:35 +09:00
Calvin Rose
06873fbf0b Update CHANGELOG.md 2025-09-20 14:30:10 -05:00
Calvin Rose
1ff26d702a Refactor flycheck to allow customization. Address #1638
Bindings can define their own flycheckers in a simple fashion.
2025-09-20 10:32:16 -05:00
Calvin Rose
4da568254a Manually declare chroot on macos. 2025-09-19 21:17:02 -05:00
Calvin Rose
357f1f94ca Add os/posix-chroot
Gated in a similar manner to os/posix-fork.
2025-09-19 16:32:45 -05:00
Calvin Rose
015e49c806 Merge pull request #1645 from pyrmont/bugfix.avoid-apc-use
Use `SetEvent` rather than `QueueUserAPC` on Windows
2025-09-19 16:31:13 -05:00
Michael Camilleri
6b06ab5f9c Remove unused function on Windows 2025-09-17 15:51:53 +09:00
Michael Camilleri
fe6c6e15a6 Add workaround to timer resolution issue on Windows 2025-09-17 15:48:37 +09:00
Michael Camilleri
b4eb52ca45 Start worker thread in suspended state on Windows 2025-09-16 11:57:40 +09:00
Michael Camilleri
aca5428846 Use SetEvent rather than QueueUserAPC on Windows 2025-09-16 10:24:24 +09:00
19 changed files with 309 additions and 156 deletions

View File

@@ -1,4 +1,4 @@
image: openbsd/7.6 image: openbsd/7.7
sources: sources:
- https://git.sr.ht/~bakpakin/janet - https://git.sr.ht/~bakpakin/janet
packages: packages:
@@ -10,7 +10,6 @@ tasks:
gmake gmake
gmake test gmake test
doas gmake install doas gmake install
gmake test-install
doas gmake uninstall doas gmake uninstall
- meson_min: | - meson_min: |
cd janet cd janet

View File

@@ -1,7 +1,11 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## Unreleased - ??? ## 1.40.0 - 2025-11-15
- Add `os/posix-chroot`
- Fix `ev/deadline` with interrupt race condition bug on Windows.
- Improve `flycheck` by allowing functions and macros to define their own flycheck behavior via the metadata `:flycheck`.
- Add `*flychecking*` dynamic binding to check if inside flycheck evalutation
- Add `gcperthread` callback for abstract types. This lets threaded abstracts have a finalizer that is called per thread, as well as a global finalizer. - Add `gcperthread` callback for abstract types. This lets threaded abstracts have a finalizer that is called per thread, as well as a global finalizer.
- Add `JANET_DO_ERROR_*` flags to describe the return value of `janet_dobytes` and `janet_dostring`. - Add `JANET_DO_ERROR_*` flags to describe the return value of `janet_dobytes` and `janet_dostring`.

View File

@@ -61,7 +61,7 @@ ensure a consistent code style for C.
## Janet style ## Janet style
All janet code in the project should be formatted similar to the code in core.janet. All janet code in the project should be formatted similar to the code in src/boot/boot.janet.
The auto formatting from janet.vim will work well. The auto formatting from janet.vim will work well.
## Typo Fixing and One-Line changes ## Typo Fixing and One-Line changes

View File

@@ -261,6 +261,7 @@ $(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS)
# Testing assumes HOSTCC=CC # Testing assumes HOSTCC=CC
TEST_SCRIPTS=$(wildcard test/suite*.janet) TEST_SCRIPTS=$(wildcard test/suite*.janet)
EXAMPLE_SCRIPTS=$(wildcard examples/*.janet)
repl: $(JANET_TARGET) repl: $(JANET_TARGET)
$(RUN) ./$(JANET_TARGET) $(RUN) ./$(JANET_TARGET)
@@ -268,21 +269,26 @@ repl: $(JANET_TARGET)
debug: $(JANET_TARGET) debug: $(JANET_TARGET)
$(DEBUGGER) ./$(JANET_TARGET) $(DEBUGGER) ./$(JANET_TARGET)
VALGRIND_COMMAND=valgrind --leak-check=full --quiet VALGRIND_COMMAND=$(RUN) valgrind --leak-check=full --quiet
CALLGRIND_COMMAND=$(RUN) valgrind --tool=callgrind
valgrind: $(JANET_TARGET) valgrind: $(JANET_TARGET)
$(VALGRIND_COMMAND) ./$(JANET_TARGET) $(VALGRIND_COMMAND) ./$(JANET_TARGET)
test: $(JANET_TARGET) $(TEST_PROGRAMS) test: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
for f in test/suite*.janet; do $(RUN) ./$(JANET_TARGET) "$$f" || exit; done for f in test/suite*.janet; do $(RUN) ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do $(RUN) ./$(JANET_TARGET) -k "$$f"; done for f in examples/*.janet; do $(RUN) ./$(JANET_TARGET) -k "$$f"; done
valtest: $(JANET_TARGET) $(TEST_PROGRAMS) valtest: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done for f in examples/*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) -k "$$f"; done
callgrind: $(JANET_TARGET) callgrind: $(JANET_TARGET)
for f in test/suite*.janet; do valgrind --tool=callgrind ./$(JANET_TARGET) "$$f" || exit; done $(CALLGRIND_COMMAND) ./$(JANET_TARGET)
calltest: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
for f in test/suite*.janet; do $(CALLGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do $(CALLGRIND_COMMAND) ./$(JANET_TARGET) -k "$$f"; done
######################## ########################
##### Distribution ##### ##### Distribution #####
@@ -413,9 +419,6 @@ clean:
-rm -rf build vgcore.* callgrind.* -rm -rf build vgcore.* callgrind.*
-rm -rf test/install/build test/install/modpath -rm -rf test/install/build test/install/modpath
test-install:
echo "JPM has been removed from default install."
help: help:
@echo @echo
@echo 'Janet: A Dynamic Language & Bytecode VM' @echo 'Janet: A Dynamic Language & Bytecode VM'
@@ -427,7 +430,8 @@ help:
@echo ' make test Test a built Janet' @echo ' make test Test a built Janet'
@echo ' make valgrind Assess Janet with Valgrind' @echo ' make valgrind Assess Janet with Valgrind'
@echo ' make callgrind Assess Janet with Valgrind, using Callgrind' @echo ' make callgrind Assess Janet with Valgrind, using Callgrind'
@echo ' make valtest Run the test suite with Valgrind to check for memory leaks' @echo ' make valtest Run the test suite and examples with Valgrind to check for memory leaks'
@echo ' make calltest Run the test suite and examples with Callgrind'
@echo ' make dist Create a distribution tarball' @echo ' make dist Create a distribution tarball'
@echo ' make docs Generate documentation' @echo ' make docs Generate documentation'
@echo ' make debug Run janet with GDB or LLDB' @echo ' make debug Run janet with GDB or LLDB'
@@ -437,6 +441,9 @@ help:
@echo " make format Format Janet's own source files" @echo " make format Format Janet's own source files"
@echo ' make grammar Generate a TextMate language grammar' @echo ' make grammar Generate a TextMate language grammar'
@echo @echo
@echo ' make install-jpm-git Install jpm into the current filesystem'
@echo ' make install-spork-git Install spork into the current filesystem'
@echo
.PHONY: clean install repl debug valgrind test \ .PHONY: clean install install-jpm-git install-spork-git repl debug valgrind test \
valtest dist uninstall docs grammar format help compile-commands valtest callgrind callgrind-test dist uninstall docs grammar format help compile-commands

View File

@@ -0,0 +1,3 @@
@{
:name "sample-bad-bundle1"
}

View File

@@ -0,0 +1,3 @@
@{
:name "sample-bad-bundle2"
}

View File

@@ -7,7 +7,7 @@
### ###
### ###
(def defn :macro (def defn :macro :flycheck
``` ```
(defn name & more) (defn name & more)
@@ -43,7 +43,7 @@
# Build return value # Build return value
~(def ,name ,;modifiers (fn ,name ,;(tuple/slice more start))))) ~(def ,name ,;modifiers (fn ,name ,;(tuple/slice more start)))))
(defn defmacro :macro (defn defmacro :macro :flycheck
"Define a macro." "Define a macro."
[name & more] [name & more]
(setdyn name @{}) # override old macro definitions in the case of a recursive macro (setdyn name @{}) # override old macro definitions in the case of a recursive macro
@@ -57,12 +57,12 @@
[f & args] [f & args]
(f ;args)) (f ;args))
(defmacro defmacro- (defmacro defmacro- :flycheck
"Define a private macro that will not be exported." "Define a private macro that will not be exported."
[name & more] [name & more]
(apply defn name :macro :private more)) (apply defn name :macro :private more))
(defmacro defn- (defmacro defn- :flycheck
"Define a private function that will not be exported." "Define a private function that will not be exported."
[name & more] [name & more]
(apply defn name :private more)) (apply defn name :private more))
@@ -144,7 +144,7 @@
(defmacro /= "Shorthand for (set x (/ x n))." [x & ns] ~(set ,x (,/ ,x ,;ns))) (defmacro /= "Shorthand for (set x (/ x n))." [x & ns] ~(set ,x (,/ ,x ,;ns)))
(defmacro %= "Shorthand for (set x (% x n))." [x & ns] ~(set ,x (,% ,x ,;ns))) (defmacro %= "Shorthand for (set x (% x n))." [x & ns] ~(set ,x (,% ,x ,;ns)))
(defmacro assert (defmacro assert :flycheck # should top level assert flycheck?
"Throw an error if x is not truthy. Will not evaluate `err` if x is truthy." "Throw an error if x is not truthy. Will not evaluate `err` if x is truthy."
[x &opt err] [x &opt err]
(def v (gensym)) (def v (gensym))
@@ -154,7 +154,7 @@
,v ,v
(,error ,(if err err (string/format "assert failure in %j" x)))))) (,error ,(if err err (string/format "assert failure in %j" x))))))
(defmacro defdyn (defmacro defdyn :flycheck
``Define an alias for a keyword that is used as a dynamic binding. The ``Define an alias for a keyword that is used as a dynamic binding. The
alias is a normal, lexically scoped binding that can be used instead of alias is a normal, lexically scoped binding that can be used instead of
a keyword to prevent typos. `defdyn` does not set dynamic bindings or otherwise a keyword to prevent typos. `defdyn` does not set dynamic bindings or otherwise
@@ -171,6 +171,9 @@
(defdyn *macro-form* (defdyn *macro-form*
"Inside a macro, is bound to the source form that invoked the macro") "Inside a macro, is bound to the source form that invoked the macro")
(defdyn *flychecking*
"Check if the current form is being evaluated inside `flycheck`. Will be `true` while flychecking.")
(defdyn *lint-error* (defdyn *lint-error*
"The current lint error level. The error level is the lint level at which compilation will exit with an error and not continue.") "The current lint error level. The error level is the lint level at which compilation will exit with an error and not continue.")
@@ -345,12 +348,15 @@
[body catch] [body catch]
(assert (and (not (empty? catch)) (indexed? (catch 0))) "the first element of `catch` must be a tuple or array") (assert (and (not (empty? catch)) (indexed? (catch 0))) "the first element of `catch` must be a tuple or array")
(let [[err fib] (catch 0) (let [[err fib] (catch 0)
r (or err (gensym)) r (gensym)
f (or fib (gensym))] f (gensym)]
~(let [,f (,fiber/new (fn :try [] ,body) :ie) ~(let [,f (,fiber/new (fn :try [] ,body) :ie)
,r (,resume ,f)] ,r (,resume ,f)]
(if (,= (,fiber/status ,f) :error) (if (,= (,fiber/status ,f) :error)
(do ,;(tuple/slice catch 1)) (do
,(if err ~(def ,err ,r))
,(if fib ~(def ,fib ,f))
,;(tuple/slice catch 1))
,r)))) ,r))))
(defmacro with-syms (defmacro with-syms
@@ -1800,8 +1806,8 @@
(flatten-into @[] xs)) (flatten-into @[] xs))
(defn kvs (defn kvs
``Takes a table or struct and returns and array of key value pairs ``Takes a table or struct and returns a new array of key value pairs
like `@[k v k v ...]`. Returns a new array.`` like `@[k v k v ...]`.``
[dict] [dict]
(def ret @[]) (def ret @[])
(loop [k :keys dict] (array/push ret k (in dict k))) (loop [k :keys dict] (array/push ret k (in dict k)))
@@ -2354,7 +2360,7 @@
(set macexvar macex) (set macexvar macex)
(defmacro varfn (defmacro varfn :flycheck
``Create a function that can be rebound. `varfn` has the same signature ``Create a function that can be rebound. `varfn` has the same signature
as `defn`, but defines functions in the environment as vars. If a var `name` as `defn`, but defines functions in the environment as vars. If a var `name`
already exists in the environment, it is rebound to the new function. Returns already exists in the environment, it is rebound to the new function. Returns
@@ -3945,7 +3951,7 @@
[& forms] [& forms]
(def state (gensym)) (def state (gensym))
(def loaded (gensym)) (def loaded (gensym))
~((fn [] ~((fn :delay []
(var ,state nil) (var ,state nil)
(var ,loaded nil) (var ,loaded nil)
(fn [] (fn []
@@ -3977,7 +3983,7 @@
:lazy lazy :lazy lazy
:map-symbols map-symbols})) :map-symbols map-symbols}))
(defmacro ffi/defbind-alias (defmacro ffi/defbind-alias :flycheck
"Generate bindings for native functions in a convenient manner. "Generate bindings for native functions in a convenient manner.
Similar to defbind but allows for the janet function name to be Similar to defbind but allows for the janet function name to be
different than the FFI function." different than the FFI function."
@@ -3988,6 +3994,8 @@
(def formal-args (map 0 arg-pairs)) (def formal-args (map 0 arg-pairs))
(def type-args (map 1 arg-pairs)) (def type-args (map 1 arg-pairs))
(def computed-type-args (eval ~[,;type-args])) (def computed-type-args (eval ~[,;type-args]))
(if (dyn *flychecking*)
(break ~(defn ,alias ,;meta [,;formal-args] nil)))
(def {:native lib (def {:native lib
:lazy lazy :lazy lazy
:native-lazy llib :native-lazy llib
@@ -4003,7 +4011,7 @@
~(defn ,alias ,;meta [,;formal-args] ~(defn ,alias ,;meta [,;formal-args]
(,ffi/call ,(make-ptr) ,(make-sig) ,;formal-args)))) (,ffi/call ,(make-ptr) ,(make-sig) ,;formal-args))))
(defmacro ffi/defbind (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))) ~(ffi/defbind-alias ,name ,name ,ret-type ,;body)))
@@ -4014,6 +4022,51 @@
### ###
### ###
(def- flycheck-specials @{})
(defn- flycheck-evaluator
``
An evaluator function that is passed to `run-context` that lints
(flychecks) code for `flycheck`. This means code will be parsed,
compiled, and have macros expanded, but the code will not be
evaluated.
``
[thunk source env where]
(when (and (tuple? source) (= (tuple/type source) :parens))
(def head (source 0))
(def entry (get env head {}))
(def fc (get flycheck-specials head (get entry :flycheck)))
(cond
# Sometimes safe form
(function? fc)
(fc thunk source env where)
# Always safe form
fc
(thunk))))
(defn flycheck
```
Check a file for errors without running the file. Found errors
will be printed to stderr in the usual format. Top level functions
and macros that have the metadata `:flycheck` will also be evaluated
during flychecking. For full control, the `:flycheck` metadata can
also be a function that takes 4 arguments - `thunk`, `source`, `env`,
and `where`, the same as the `:evaluator` argument to `run-context`.
Other arguments to `flycheck` are the same as `dofile`. Returns nil.
```
[path &keys kwargs]
(def mc @{})
(def new-env (make-env (get kwargs :env)))
(put new-env *flychecking* true)
(put new-env *module-cache* @{})
(put new-env *module-loading* @{})
(put new-env *module-make-env* (fn :make-flycheck-env [&] (make-env new-env)))
(try
(dofile path :evaluator flycheck-evaluator ;(kvs kwargs) :env new-env)
([e f]
(debug/stacktrace f e "")))
nil)
(defn- no-side-effects (defn- no-side-effects
`Check if form may have side effects. If returns true, then the src `Check if form may have side effects. If returns true, then the src
must not have side effects, such as calling a C function.` must not have side effects, such as calling a C function.`
@@ -4029,59 +4082,29 @@
(all no-side-effects (values src))) (all no-side-effects (values src)))
true)) true))
(defn- is-safe-def [x] (no-side-effects (last x))) (defn- is-safe-def [thunk source env where]
(if (no-side-effects (last source))
(thunk)))
(def- safe-forms {'defn true 'varfn true 'defn- true 'defmacro true 'defmacro- true (defn- flycheck-importer
'def is-safe-def 'var is-safe-def 'def- is-safe-def 'var- is-safe-def
'defglobal is-safe-def 'varglobal is-safe-def 'defdyn true})
(def- importers {'import true 'import* true 'dofile true 'require true})
(defn- use-2 [evaluator args]
(each a args (import* (string a) :prefix "" :evaluator evaluator)))
(defn- flycheck-evaluator
``An evaluator function that is passed to `run-context` that lints (flychecks) code.
This means code will parsed and compiled, macros executed, but the code will not be run.
Used by `flycheck`.``
[thunk source env where] [thunk source env where]
(when (tuple? source)
(def head (source 0))
(def safe-check
(or
(safe-forms head)
(if (symbol? head)
(if (string/has-prefix? "define-" head) is-safe-def))))
(cond
# Sometimes safe form
(function? safe-check)
(if (safe-check source) (thunk))
# Always safe form
safe-check
(thunk)
# Use
(= 'use head)
(use-2 flycheck-evaluator (tuple/slice source 1))
# Import-like form
(importers head)
(let [[l c] (tuple/sourcemap source) (let [[l c] (tuple/sourcemap source)
newtup (tuple/setmap (tuple ;source :evaluator flycheck-evaluator) l c)] newtup (tuple/setmap (tuple ;source :evaluator flycheck-evaluator) l c)]
((compile newtup env where)))))) ((compile newtup env where))))
(defn flycheck (defn- flycheck-use
``Check a file for errors without running the file. Found errors will be printed to stderr [thunk source env where]
in the usual format. Macros will still be executed, however, so (each a (drop 1 source) (import* (string a) :prefix "" :evaluator flycheck-evaluator)))
arbitrary execution is possible. Other arguments are the same as `dofile`. `path` can also be
a file value such as stdin. Returns nil.`` # Add metadata to defs and import macros for flychecking
[path &keys kwargs] (each sym ['def 'var]
(def old-modcache (table/clone module/cache)) (put flycheck-specials sym is-safe-def))
(table/clear module/cache) (each sym ['def- 'var- 'defglobal 'varglobal]
(try (put (dyn sym) :flycheck is-safe-def))
(dofile path :evaluator flycheck-evaluator ;(kvs kwargs)) (each sym ['import 'import* 'dofile 'require]
([e f] (put (dyn sym) :flycheck flycheck-importer))
(debug/stacktrace f e ""))) (each sym ['use]
(table/clear module/cache) (put (dyn sym) :flycheck flycheck-use))
(merge-into module/cache old-modcache)
nil)
### ###
### ###
@@ -4174,7 +4197,7 @@
(spit manifest-name b)) (spit manifest-name b))
(defn bundle/manifest (defn bundle/manifest
"Get the manifest for a give installed bundle" "Get the manifest for a given installed bundle."
[bundle-name] [bundle-name]
(def name (get-manifest-filename bundle-name)) (def name (get-manifest-filename bundle-name))
(assertf (fexists name) "no bundle %v found" bundle-name) (assertf (fexists name) "no bundle %v found" bundle-name)
@@ -4199,7 +4222,9 @@
(put new-env *syspath* fixed-syspath) (put new-env *syspath* fixed-syspath)
(with-env new-env (with-env new-env
(put new-env :bundle-dir (bundle-dir bundle-name)) # get the syspath right (put new-env :bundle-dir (bundle-dir bundle-name)) # get the syspath right
(require (string "@syspath/bundle/" bundle-name))))) (try
(require (string "@syspath/bundle/" bundle-name))
([_] (error "bundle must contain bundle.janet or bundle/init.janet"))))))
(defn- do-hook (defn- do-hook
[module bundle-name hook & args] [module bundle-name hook & args]
@@ -4236,7 +4261,9 @@
nil) nil)
(defn bundle/uninstall (defn bundle/uninstall
"Remove a bundle from the current syspath" ``Remove a bundle from the current syspath. There is 1 hook called during
uninstallation (uninstall). A user can register a hook by defining a
function with the same name in the bundle script.``
[bundle-name] [bundle-name]
(def breakage @{}) (def breakage @{})
(each b (bundle/list) (each b (bundle/list)
@@ -4272,8 +4299,8 @@
order) order)
(defn bundle/prune (defn bundle/prune
"Remove all orphaned bundles from the syspath. An orphaned bundle is a bundle that is ``Remove all orphaned bundles from the current syspath. An orphaned bundle is a
marked for :auto-remove and is not depended on by any other bundle." bundle that is marked for :auto-remove and is not depended on by any other bundle.``
[] []
(def topo (bundle/topolist)) (def topo (bundle/topolist))
(def rtopo (reverse topo)) (def rtopo (reverse topo))
@@ -4302,33 +4329,44 @@
(not (not (os/stat (bundle-dir bundle-name) :mode)))) (not (not (os/stat (bundle-dir bundle-name) :mode))))
(defn bundle/install (defn bundle/install
"Install a bundle from the local filesystem. The name of the bundle will be inferred from the bundle, or passed as a parameter :name in `config`." ``Install a bundle from the local filesystem. The name of the bundle is
the value mapped to :name in either `config` or the info file. There are
5 hooks called during installation (postdeps, clean, build, install and
check). A user can register a hook by defining a function with the same name
in the bundle script.``
[path &keys config] [path &keys config]
(def path (bundle-rpath path)) (def path (bundle-rpath path))
(def s (sep)) (def s (sep))
# Detect bundle name # Detect bundle name
(def infofile-src1 (string path s "bundle" s "info.jdn")) (def infofile-src1 (string path s "bundle" s "info.jdn"))
(def infofile-src2 (string path s "info.jdn")) (def infofile-src2 (string path s "info.jdn"))
(def infofile-src (cond (fexists infofile-src1) infofile-src1 (def infofile-src (cond
(fexists infofile-src1) infofile-src1
(fexists infofile-src2) infofile-src2)) (fexists infofile-src2) infofile-src2))
(def info (-?> infofile-src slurp parse)) (def info (-?> infofile-src slurp parse))
(def bundle-name (get config :name (get info :name))) (def bundle-name (get config :name (get info :name)))
(assertf bundle-name "unable to infer bundle name for %v, use :name argument" path) (assertf bundle-name
"unable to infer bundle name for %v, use :name argument or add :name to info file" path)
(assertf (not (string/check-set "\\/" bundle-name)) (assertf (not (string/check-set "\\/" bundle-name))
"bundle name %v cannot contain path separators" bundle-name) "bundle name %v cannot contain path separators" bundle-name)
(assert (next bundle-name) "cannot use empty bundle-name") (assert (next bundle-name) "cannot use empty bundle-name")
(assertf (not (fexists (get-manifest-filename bundle-name))) (assertf (not (fexists (get-manifest-filename bundle-name)))
"bundle %v is already installed" bundle-name) "bundle %v is already installed" bundle-name)
# Check bscript
(def bscript-src1 (string path s "bundle" s "init.janet"))
(def bscript-src2 (string path s "bundle.janet"))
(def bscript-src (cond
(fexists bscript-src1) bscript-src1
(fexists bscript-src2) bscript-src2))
# Setup installed paths # Setup installed paths
(prime-bundle-paths) (prime-bundle-paths)
(os/mkdir (bundle-dir bundle-name)) (os/mkdir (bundle-dir bundle-name))
# Copy infofile # Copy aliased infofile
(def infofile-dest (bundle-file bundle-name "info.jdn")) (when (fexists infofile-src2)
(when infofile-src (copyfile infofile-src infofile-dest)) (copyfile infofile-src2 (bundle-file bundle-name "info.jdn")))
# Copy aliased initfile # Copy aliased bscript
(def initfile-alias (string path s "bundle.janet")) (when (fexists bscript-src2)
(def initfile-dest (bundle-file bundle-name "init.janet")) (copyfile bscript-src2 (bundle-file bundle-name "init.janet")))
(when (fexists initfile-alias) (copyfile initfile-alias initfile-dest))
# Copy some files into the new location unconditionally # Copy some files into the new location unconditionally
(def implicit-sources (string path s "bundle")) (def implicit-sources (string path s "bundle"))
(when (= :directory (os/stat implicit-sources :mode)) (when (= :directory (os/stat implicit-sources :mode))
@@ -4337,8 +4375,7 @@
(merge-into man config) (merge-into man config)
(sync-manifest man) (sync-manifest man)
(edefer (do (print "installation error, uninstalling") (bundle/uninstall bundle-name)) (edefer (do (print "installation error, uninstalling") (bundle/uninstall bundle-name))
(when (os/stat infofile-dest :mode) (when info
(def info (-> infofile-dest slurp parse))
(def deps (seq [d :in (get info :dependencies @[])] (def deps (seq [d :in (get info :dependencies @[])]
(string (if (dictionary? d) (get d :name) d)))) (string (if (dictionary? d) (get d :name) d))))
(def missing (filter (complement bundle/installed?) deps)) (def missing (filter (complement bundle/installed?) deps))
@@ -4346,12 +4383,13 @@
(error (string "missing dependencies " (string/join missing ", ")))) (error (string "missing dependencies " (string/join missing ", "))))
(put man :dependencies deps) (put man :dependencies deps)
(put man :info info)) (put man :info info))
(def module (get-bundle-module bundle-name))
(def clean (get config :clean)) (def clean (get config :clean))
(def check (get config :check)) (def check (get config :check))
(def module (get-bundle-module bundle-name))
(def all-hooks (seq [[k v] :pairs module :when (symbol? k) :unless (get v :private)] (keyword k))) (def all-hooks (seq [[k v] :pairs module :when (symbol? k) :unless (get v :private)] (keyword k)))
(put man :hooks all-hooks) (put man :hooks all-hooks)
(do-hook module bundle-name :dependencies man) (do-hook module bundle-name :dependencies man) # deprecated, use :postdeps
(do-hook module bundle-name :postdeps man)
(when clean (when clean
(do-hook module bundle-name :clean man)) (do-hook module bundle-name :clean man))
(do-hook module bundle-name :build man) (do-hook module bundle-name :build man)
@@ -4361,15 +4399,21 @@
(when check (when check
(do-hook module bundle-name :check man))) (do-hook module bundle-name :check man)))
(print "installed " bundle-name) (print "installed " bundle-name)
(when (get man :has-bin-script) (when (or (get man :has-exe)
# remove eventually
(get man :has-bin-script))
(def binpath (string (dyn *syspath*) s "bin")) (def binpath (string (dyn *syspath*) s "bin"))
(eprintf "executable scripts have been installed to %s" binpath)) (eprintf "executable files have been installed to %s" binpath))
(when (get man :has-man)
(def manpath (string (dyn *syspath*) s "man"))
(eprintf "man pages have been installed to %s" manpath))
bundle-name) bundle-name)
(defn- bundle/pack (defn- bundle/pack
"Take an installed bundle and create a bundle source directory that can be used to ``Take an installed bundle and create a bundle source directory that can be
reinstall the bundle on a compatible system. This is used to create backups for installed used to reinstall the bundle on a compatible system. This is used to create
bundles without rebuilding, or make a prebuilt bundle for other systems." backups for installed bundles without rebuilding, or make a prebuilt bundle
for other systems.``
[bundle-name dest-dir &opt is-backup] [bundle-name dest-dir &opt is-backup]
(var i 0) (var i 0)
(def man (bundle/manifest bundle-name)) (def man (bundle/manifest bundle-name))
@@ -4399,9 +4443,9 @@
dest-dir) dest-dir)
(defn bundle/replace (defn bundle/replace
"Reinstall an existing bundle from a new directory. Similar to bundle/reinstall, ``Reinstall an existing bundle from a new directory. Similar to
but installs the replacement bundle from any directory. This is necesarry to replace a package without bundle/reinstall, but installs the replacement bundle from any directory.
breaking any dependencies." This is necessary to replace a package without breaking any dependencies.``
[bundle-name path &keys new-config] [bundle-name path &keys new-config]
(def manifest (bundle/manifest bundle-name)) (def manifest (bundle/manifest bundle-name))
(def config (get manifest :config @{})) (def config (get manifest :config @{}))
@@ -4428,7 +4472,7 @@
bundle-name) bundle-name)
(defn bundle/add-directory (defn bundle/add-directory
"Add a directory during the install process relative to `(dyn *syspath*)`" "Add a directory during an install relative to `(dyn *syspath*)`."
[manifest dest &opt chmod-mode] [manifest dest &opt chmod-mode]
(def files (get-files manifest)) (def files (get-files manifest))
(def s (sep)) (def s (sep))
@@ -4456,7 +4500,7 @@
ret) ret)
(defn bundle/add-file (defn bundle/add-file
"Add files during an install relative to `(dyn *syspath*)`" "Add a file during an install relative to `(dyn *syspath*)`."
[manifest src &opt dest chmod-mode] [manifest src &opt dest chmod-mode]
(default dest src) (default dest src)
(def files (get-files manifest)) (def files (get-files manifest))
@@ -4473,9 +4517,9 @@
absdest) absdest)
(defn bundle/add (defn bundle/add
"Add files and directories during a bundle install relative to `(dyn *syspath*)`. ``Add a file or directory during an install relative to `(dyn *syspath*)`.
Added files and directories will be recorded in the bundle manifest such that they are properly tracked Added files and directories will be recorded in the bundle manifest such
and removed during an upgrade or uninstall." that they are properly tracked and removed during an upgrade or uninstall.``
[manifest src &opt dest chmod-mode] [manifest src &opt dest chmod-mode]
(default dest src) (default dest src)
(def s (sep)) (def s (sep))
@@ -4490,20 +4534,31 @@
(errorf "bad path %s - file is a %s" src mode))) (errorf "bad path %s - file is a %s" src mode)))
(defn bundle/add-bin (defn bundle/add-bin
`` ``Add a file to the "bin" subdirectory of the current syspath. By default,
Shorthand for adding scripts during an install. Scripts will be installed to files will be set to be executable.``
`(string (dyn *syspath*) "/bin")` by default and will be set to be executable. [manifest src &opt filename chmod-mode]
``
[manifest src &opt dest chmod-mode]
(def s (sep)) (def s (sep))
(default dest (last (string/split s src))) (default filename (last (string/split s src)))
(default chmod-mode 8r755) (default chmod-mode 8r755)
(os/mkdir (string (dyn *syspath*) s "bin")) (os/mkdir (string (dyn *syspath*) s "bin"))
(put manifest :has-bin-script true) (put manifest :has-exe true)
(bundle/add-file manifest src (string "bin" s dest) chmod-mode)) (put manifest :has-bin-script true) # remove eventually
(bundle/add-file manifest src (string "bin" s filename) chmod-mode))
(defn bundle/add-manpage
``Add a file to the man subdirectory of the current syspath. Files are
copied inside a directory `mansec`. By default, `mansec` is "man1".``
[manifest src &opt mansec]
(def s (sep))
(default mansec "man1")
(def filename (last (string/split s src)))
(os/mkdir (string (dyn *syspath*) s "man"))
(os/mkdir (string (dyn *syspath*) s "man" s mansec))
(put manifest :has-man true)
(bundle/add-file manifest src (string "man" s mansec s filename)))
(defn bundle/update-all (defn bundle/update-all
"Reinstall all bundles" "Reinstall all bundles."
[&keys configs] [&keys configs]
(each bundle (bundle/topolist) (each bundle (bundle/topolist)
(bundle/reinstall bundle ;(kvs configs))))) (bundle/reinstall bundle ;(kvs configs)))))
@@ -4515,7 +4570,10 @@
### ###
# conditional compilation for reduced os # conditional compilation for reduced os
(def- getenv-alias (if-let [entry (in root-env 'os/getenv)] (entry :value) (fn [&]))) (def- getenv-raw (if-let [entry (in root-env 'os/getenv)] (entry :value) (fn [&])))
(defn- getenv-alias [env-var &opt dflt]
(def x (getenv-raw env-var dflt))
(if (= x "") nil x)) # empty string is coerced to nil
(defn- run-main (defn- run-main
[env subargs arg] [env subargs arg]
@@ -4890,14 +4948,15 @@
"src/core/wrap.c"]) "src/core/wrap.c"])
# Print janet.c to stdout # Print janet.c to stdout
(print "/* Amalgamated build - DO NOT EDIT */") (def image-only (has-value? boot/args "image-only"))
(print "/* " (if image-only "Image-only" "Amalgamated") " build - DO NOT EDIT */")
(print "/* Generated from janet version " janet/version "-" janet/build " */") (print "/* Generated from janet version " janet/version "-" janet/build " */")
(print "#define JANET_BUILD \"" janet/build "\"") (print "#define JANET_BUILD \"" janet/build "\"")
(print ```#define JANET_AMALG```) (print ```#define JANET_AMALG```)
(defn do-one-file (defn do-one-file
[fname] [fname]
(unless (has-value? boot/args "image-only") (unless image-only
(print "\n/* " fname " */") (print "\n/* " fname " */")
(print "#line 0 \"" fname "\"\n") (print "#line 0 \"" fname "\"\n")
(def source (slurp fname)) (def source (slurp fname))

View File

@@ -88,7 +88,7 @@ void *janet_abstract_threaded(const JanetAbstractType *atype, size_t size) {
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
size_t janet_os_mutex_size(void) { size_t janet_os_mutex_size(void) {
return sizeof(SRWLOCK); return sizeof(CRITICAL_SECTION);
} }
size_t janet_os_rwlock_size(void) { size_t janet_os_rwlock_size(void) {
@@ -96,20 +96,20 @@ size_t janet_os_rwlock_size(void) {
} }
void janet_os_mutex_init(JanetOSMutex *mutex) { void janet_os_mutex_init(JanetOSMutex *mutex) {
InitializeSRWLock((PSRWLOCK) mutex); InitializeCriticalSection((CRITICAL_SECTION *) mutex);
} }
void janet_os_mutex_deinit(JanetOSMutex *mutex) { void janet_os_mutex_deinit(JanetOSMutex *mutex) {
/* no op? */ DeleteCriticalSection((CRITICAL_SECTION *) mutex);
(void) mutex;
} }
void janet_os_mutex_lock(JanetOSMutex *mutex) { void janet_os_mutex_lock(JanetOSMutex *mutex) {
AcquireSRWLockExclusive((PSRWLOCK) mutex); EnterCriticalSection((CRITICAL_SECTION *) mutex);
} }
void janet_os_mutex_unlock(JanetOSMutex *mutex) { void janet_os_mutex_unlock(JanetOSMutex *mutex) {
ReleaseSRWLockExclusive((PSRWLOCK) mutex); /* error handling? May want to keep counter */
LeaveCriticalSection((CRITICAL_SECTION *) mutex);
} }
void janet_os_rwlock_init(JanetOSRWLock *rwlock) { void janet_os_rwlock_init(JanetOSRWLock *rwlock) {

View File

@@ -746,6 +746,7 @@ typedef struct SandboxOption {
static const SandboxOption sandbox_options[] = { static const SandboxOption sandbox_options[] = {
{"all", JANET_SANDBOX_ALL}, {"all", JANET_SANDBOX_ALL},
{"chroot", JANET_SANDBOX_CHROOT},
{"env", JANET_SANDBOX_ENV}, {"env", JANET_SANDBOX_ENV},
{"ffi", JANET_SANDBOX_FFI}, {"ffi", JANET_SANDBOX_FFI},
{"ffi-define", JANET_SANDBOX_FFI_DEFINE}, {"ffi-define", JANET_SANDBOX_FFI_DEFINE},
@@ -771,6 +772,7 @@ JANET_CORE_FN(janet_core_sandbox,
"Disable feature sets to prevent the interpreter from using certain system resources. " "Disable feature sets to prevent the interpreter from using certain system resources. "
"Once a feature is disabled, there is no way to re-enable it. Capabilities can be:\n\n" "Once a feature is disabled, there is no way to re-enable it. Capabilities can be:\n\n"
"* :all - disallow all (except IO to stdout, stderr, and stdin)\n" "* :all - disallow all (except IO to stdout, stderr, and stdin)\n"
"* :chroot - disallow calling `os/posix-chroot`\n"
"* :env - disallow reading and write env variables\n" "* :env - disallow reading and write env variables\n"
"* :ffi - disallow FFI (recommended if disabling anything else)\n" "* :ffi - disallow FFI (recommended if disabling anything else)\n"
"* :ffi-define - disallow loading new FFI modules and binding new functions\n" "* :ffi-define - disallow loading new FFI modules and binding new functions\n"

View File

@@ -83,7 +83,7 @@ struct JanetChannel {
int closed; int closed;
int is_threaded; int is_threaded;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
SRWLOCK lock; CRITICAL_SECTION lock;
#else #else
pthread_mutex_t lock; pthread_mutex_t lock;
#endif #endif
@@ -117,6 +117,9 @@ typedef struct {
double sec; double sec;
JanetVM *vm; JanetVM *vm;
JanetFiber *fiber; JanetFiber *fiber;
#ifdef JANET_WINDOWS
HANDLE cancel_event;
#endif
} JanetThreadedTimeout; } JanetThreadedTimeout;
#define JANET_MAX_Q_CAPACITY 0x7FFFFFF #define JANET_MAX_Q_CAPACITY 0x7FFFFFF
@@ -604,12 +607,7 @@ void janet_ev_init_common(void) {
#endif #endif
} }
#ifdef JANET_WINDOWS #if JANET_ANDROID
static VOID CALLBACK janet_timeout_stop(ULONG_PTR ptr) {
UNREFERENCED_PARAMETER(ptr);
ExitThread(0);
}
#elif JANET_ANDROID
static void janet_timeout_stop(int sig_num) { static void janet_timeout_stop(int sig_num) {
if (sig_num == SIGUSR1) { if (sig_num == SIGUSR1) {
pthread_exit(0); pthread_exit(0);
@@ -620,10 +618,14 @@ static void janet_timeout_stop(int sig_num) {
static void handle_timeout_worker(JanetTimeout to, int cancel) { static void handle_timeout_worker(JanetTimeout to, int cancel) {
if (!to.has_worker) return; if (!to.has_worker) return;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
(void) cancel; if (cancel && to.worker_event) {
QueueUserAPC(janet_timeout_stop, to.worker, 0); SetEvent(to.worker_event);
}
WaitForSingleObject(to.worker, INFINITE); WaitForSingleObject(to.worker, INFINITE);
CloseHandle(to.worker); CloseHandle(to.worker);
if (to.worker_event) {
CloseHandle(to.worker_event);
}
#else #else
#ifdef JANET_ANDROID #ifdef JANET_ANDROID
if (cancel) janet_assert(!pthread_kill(to.worker, SIGUSR1), "pthread_kill"); if (cancel) janet_assert(!pthread_kill(to.worker, SIGUSR1), "pthread_kill");
@@ -693,10 +695,20 @@ static void janet_timeout_cb(JanetEVGenericMessage msg) {
static DWORD WINAPI janet_timeout_body(LPVOID ptr) { static DWORD WINAPI janet_timeout_body(LPVOID ptr) {
JanetThreadedTimeout tto = *(JanetThreadedTimeout *)ptr; JanetThreadedTimeout tto = *(JanetThreadedTimeout *)ptr;
janet_free(ptr); janet_free(ptr);
SleepEx((DWORD)(tto.sec * 1000), TRUE); JanetTimestamp wait_begin = ts_now();
DWORD duration = (DWORD)round(tto.sec * 1000);
DWORD res = WAIT_TIMEOUT;
JanetTimestamp wait_end = ts_now();
for (DWORD i = 1; res == WAIT_TIMEOUT && (wait_end - wait_begin) < duration; i++) {
res = WaitForSingleObject(tto.cancel_event, (duration + i));
wait_end = ts_now();
}
/* only send interrupt message if result is WAIT_TIMEOUT */
if (res == WAIT_TIMEOUT) {
janet_interpreter_interrupt(tto.vm); janet_interpreter_interrupt(tto.vm);
JanetEVGenericMessage msg = {0}; JanetEVGenericMessage msg = {0};
janet_ev_post_event(tto.vm, janet_timeout_cb, msg); janet_ev_post_event(tto.vm, janet_timeout_cb, msg);
}
return 0; return 0;
} }
#else #else
@@ -3270,7 +3282,13 @@ JANET_CORE_FN(cfun_ev_deadline,
tto->vm = &janet_vm; tto->vm = &janet_vm;
tto->fiber = tocheck; tto->fiber = tocheck;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
HANDLE worker = CreateThread(NULL, 0, janet_timeout_body, tto, 0, NULL); HANDLE cancel_event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == cancel_event) {
janet_free(tto);
janet_panic("failed to create cancel event");
}
tto->cancel_event = cancel_event;
HANDLE worker = CreateThread(NULL, 0, janet_timeout_body, tto, CREATE_SUSPENDED, NULL);
if (NULL == worker) { if (NULL == worker) {
janet_free(tto); janet_free(tto);
janet_panic("failed to create thread"); janet_panic("failed to create thread");
@@ -3285,6 +3303,10 @@ JANET_CORE_FN(cfun_ev_deadline,
#endif #endif
to.has_worker = 1; to.has_worker = 1;
to.worker = worker; to.worker = worker;
#ifdef JANET_WINDOWS
to.worker_event = cancel_event;
ResumeThread(worker);
#endif
} else { } else {
to.has_worker = 0; to.has_worker = 0;
} }

View File

@@ -67,6 +67,7 @@
#include <crt_externs.h> #include <crt_externs.h>
#define environ (*_NSGetEnviron()) #define environ (*_NSGetEnviron())
#include <AvailabilityMacros.h> #include <AvailabilityMacros.h>
int chroot(const char *dirname);
#else #else
extern char **environ; extern char **environ;
#endif #endif
@@ -1541,6 +1542,28 @@ JANET_CORE_FN(os_posix_fork,
#endif #endif
} }
JANET_CORE_FN(os_posix_chroot,
"(os/posix-chroot dirname)",
"Call `chroot` to change the root directory to `dirname`. "
"Not supported on all systems (POSIX only).") {
janet_sandbox_assert(JANET_SANDBOX_CHROOT);
janet_fixarity(argc, 1);
#ifdef JANET_WINDOWS
(void) argv;
janet_panic("not supported on Windows");
#else
const char *root = janet_getcstring(argv, 0);
int result;
do {
result = chroot(root);
} while (result == -1 && errno == EINTR);
if (result == -1) {
janet_panic(janet_strerror(errno));
}
return janet_wrap_nil();
#endif
}
#ifdef JANET_EV #ifdef JANET_EV
/* Runs in a separate thread */ /* Runs in a separate thread */
static JanetEVGenericMessage os_shell_subr(JanetEVGenericMessage args) { static JanetEVGenericMessage os_shell_subr(JanetEVGenericMessage args) {
@@ -2849,6 +2872,7 @@ void janet_lib_os(JanetTable *env) {
JANET_CORE_REG("os/touch", os_touch), JANET_CORE_REG("os/touch", os_touch),
JANET_CORE_REG("os/realpath", os_realpath), JANET_CORE_REG("os/realpath", os_realpath),
JANET_CORE_REG("os/cd", os_cd), JANET_CORE_REG("os/cd", os_cd),
JANET_CORE_REG("os/posix-chroot", os_posix_chroot),
#ifndef JANET_NO_UMASK #ifndef JANET_NO_UMASK
JANET_CORE_REG("os/umask", os_umask), JANET_CORE_REG("os/umask", os_umask),
#endif #endif

View File

@@ -60,7 +60,6 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
done = 1; done = 1;
} }
} else { } else {
ret = janet_wrap_string(cres.error);
int32_t line = (int32_t) parser->line; int32_t line = (int32_t) parser->line;
int32_t col = (int32_t) parser->column; int32_t col = (int32_t) parser->column;
if ((cres.error_mapping.line > 0) && if ((cres.error_mapping.line > 0) &&
@@ -68,13 +67,17 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
line = cres.error_mapping.line; line = cres.error_mapping.line;
col = cres.error_mapping.column; col = cres.error_mapping.column;
} }
JanetString ctx = janet_formatc("%s:%d:%d: compile error",
sourcePath, line, col);
JanetString errstr = janet_formatc("%s: %s",
(const char *)ctx,
(const char *)cres.error);
ret = janet_wrap_string(errstr);
if (cres.macrofiber) { if (cres.macrofiber) {
janet_eprintf("%s:%d:%d: compile error", sourcePath, janet_eprintf("%s", (const char *)ctx);
line, col);
janet_stacktrace_ext(cres.macrofiber, ret, ""); janet_stacktrace_ext(cres.macrofiber, ret, "");
} else { } else {
janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath, janet_eprintf("%s\n", (const char *)errstr);
line, col, (const char *)cres.error);
} }
errflags |= JANET_DO_ERROR_COMPILE; errflags |= JANET_DO_ERROR_COMPILE;
done = 1; done = 1;
@@ -89,12 +92,14 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
done = 1; done = 1;
break; break;
case JANET_PARSE_ERROR: { case JANET_PARSE_ERROR: {
const char *e = janet_parser_error(parser);
errflags |= JANET_DO_ERROR_PARSE; errflags |= JANET_DO_ERROR_PARSE;
ret = janet_cstringv(e);
int32_t line = (int32_t) parser->line; int32_t line = (int32_t) parser->line;
int32_t col = (int32_t) parser->column; int32_t col = (int32_t) parser->column;
janet_eprintf("%s:%d:%d: parse error: %s\n", sourcePath, line, col, e); JanetString errstr = janet_formatc("%s:%d:%d: parse error: %s",
sourcePath, line, col,
janet_parser_error(parser));
ret = janet_wrap_string(errstr);
janet_eprintf("%s\n", (const char *)errstr);
done = 1; done = 1;
break; break;
} }
@@ -122,6 +127,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
janet_loop(); janet_loop();
if (fiber) { if (fiber) {
janet_gcunroot(janet_wrap_fiber(fiber)); janet_gcunroot(janet_wrap_fiber(fiber));
if (!errflags)
ret = fiber->last_value; ret = fiber->last_value;
} }
} }

View File

@@ -68,6 +68,7 @@ typedef struct {
int has_worker; int has_worker;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
HANDLE worker; HANDLE worker;
HANDLE worker_event;
#else #else
pthread_t worker; pthread_t worker;
#endif #endif

View File

@@ -147,6 +147,7 @@ extern "C" {
|| defined(__s390x__) /* S390 64-bit */ \ || defined(__s390x__) /* S390 64-bit */ \
|| defined(__s390__) /* S390 32-bit */ \ || defined(__s390__) /* S390 32-bit */ \
|| defined(__ARMEB__) /* ARM big endian */ \ || defined(__ARMEB__) /* ARM big endian */ \
|| defined(__AARCH64EB__) /* ARM64 big endian */ \
|| ((defined(__CC_ARM) || defined(__ARMCC__)) /* ARM RealView compiler */ \ || ((defined(__CC_ARM) || defined(__ARMCC__)) /* ARM RealView compiler */ \
&& defined(__BIG_ENDIAN)) && defined(__BIG_ENDIAN))
#define JANET_BIG_ENDIAN 1 #define JANET_BIG_ENDIAN 1
@@ -1899,6 +1900,7 @@ JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *pr
#define JANET_SANDBOX_FFI_USE 2048 #define JANET_SANDBOX_FFI_USE 2048
#define JANET_SANDBOX_FFI_JIT 4096 #define JANET_SANDBOX_FFI_JIT 4096
#define JANET_SANDBOX_SIGNAL 8192 #define JANET_SANDBOX_SIGNAL 8192
#define JANET_SANDBOX_CHROOT 16384
#define JANET_SANDBOX_FFI (JANET_SANDBOX_FFI_DEFINE | JANET_SANDBOX_FFI_USE | JANET_SANDBOX_FFI_JIT) #define JANET_SANDBOX_FFI (JANET_SANDBOX_FFI_DEFINE | JANET_SANDBOX_FFI_USE | JANET_SANDBOX_FFI_JIT)
#define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP) #define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP)
#define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN) #define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN)

View File

@@ -50,6 +50,11 @@
(def errsym (keyword (gensym))) (def errsym (keyword (gensym)))
~(assert (= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg)) ~(assert (= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg))
(defmacro assert-error-value
[msg errval & forms]
(def e (gensym))
~(assert (= ,errval (try (do ,;forms) ([,e] ,e))) ,msg))
(defn check-compile-error (defn check-compile-error
[form] [form]
(def result (compile form)) (def result (compile form))

View File

@@ -1023,4 +1023,11 @@
(assert (deep-not= @{:key1 "value1" [@"key2"] @"value2"} (assert (deep-not= @{:key1 "value1" [@"key2"] @"value2"}
@{:key1 "value1" [@"key2"] @"value2"}) "deep= mutable keys") @{:key1 "value1" [@"key2"] @"value2"}) "deep= mutable keys")
# different try overloads
(assert (= (try (error :error) ([] :caught)) :caught))
(assert (= (try (error :error) ([e] e)) :error))
(assert (= (try (error :error) ([e fib] [e (fiber? fib)])) [:error true]))
# regression test for #1659
(assert (= (try (error :error) ([_ _] :caught)) :caught))
(end-suite) (end-suite)

View File

@@ -117,8 +117,17 @@
(assert (= 0 (length (bundle/list))) "bundles are listed correctly 7") (assert (= 0 (length (bundle/list))) "bundles are listed correctly 7")
(assert (= 0 (length (bundle/topolist))) "bundles are listed correctly 8") (assert (= 0 (length (bundle/topolist))) "bundles are listed correctly 8")
# Try installing a bundle that is missing bundle script
(assert-error-value "bundle missing bundle script"
"bundle must contain bundle.janet or bundle/init.janet"
(bundle/install "./examples/sample-bad-bundle1"))
(assert (= 0 (length (bundle/list))) "check failure 0")
(assert (= 0 (length (bundle/topolist))) "check failure 1")
# Try installing a bundle that fails check # Try installing a bundle that fails check
(assert-error "bad test" (bundle/install "./examples/sample-bad-bundle" :check true)) (assert-error-value "bundle check hook fails"
"Check failed!"
(bundle/install "./examples/sample-bad-bundle2" :check true))
(assert (= 0 (length (bundle/list))) "check failure 0") (assert (= 0 (length (bundle/list))) "check failure 0")
(assert (= 0 (length (bundle/topolist))) "check failure 1") (assert (= 0 (length (bundle/topolist))) "check failure 1")