Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

dlopen(), random, I/O, eval, and co-expressions (co-routines) #1843

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f8ea3b3
Allow jq --run-tests to express error expectations
nicowilliams Dec 29, 2019
850e0a8
Silence warning in src/jv_dtoa_tsd.c
nicowilliams Dec 28, 2019
9bb6bfa
Support builtin modules and C-coded ext. modules
nicowilliams Mar 10, 2019
bbd61b3
Add RNG builtins
nicowilliams Mar 12, 2019
d4ef97e
Add infrastructure for file handles
nicowilliams Mar 16, 2019
a9f05a5
Add "jq/io" builtin module and -I / --io CLI opt
nicowilliams Mar 16, 2019
436a42b
Add "jq/proc" builtin module
nicowilliams Mar 16, 2019
8b4f38b
Co-expressions (co-routines) and eval
nicowilliams Mar 20, 2019
bb7f685
Add `unwind(protect)`
nicowilliams Mar 28, 2019
e3d8945
Careful with jv_number_random_int(); it might fail
nicowilliams Apr 3, 2019
ccce953
Stop leaking break "errors"
nicowilliams Apr 3, 2019
cce2728
Make debugging slightly easier with enum name
nicowilliams Apr 3, 2019
b647fd3
Make it impossible to catch break errors
nicowilliams Apr 4, 2019
2fe0ff3
Add scripts/dwarf2h
nicowilliams Jul 9, 2019
148345a
Fix try/catch catches more than it should #1859
nicowilliams May 26, 2019
300f85b
Add try-catch-finally and fix #1608
nicowilliams Dec 29, 2019
0a09a94
WIP: Add --io-policy option
nicowilliams Dec 31, 2019
3c09ac6
WIP: Add proper process spawn utilities
nicowilliams Jan 1, 2020
df08f05
WIP: Add high-priority pipe operator
nicowilliams Jan 1, 2020
f339f1d
malloc(0) can return 0 (Fix #2003)
nicowilliams Jan 1, 2020
2ddd800
macos-compatible usage of `environ`
leonid-s-usov Jan 21, 2020
c53fadf
block_inline: multi-inst inlining of functions with arguments
leonid-s-usov Jan 24, 2020
d31baed
WIP: coroutines++: new instruction OUT, revision of TOP, RET
leonid-s-usov Jan 25, 2020
45603e5
Schroedinger's TOP: hello and goodbye
leonid-s-usov Jan 25, 2020
9675459
oops
leonid-s-usov Jan 25, 2020
ad89e99
builtins: introduce public inlines
leonid-s-usov Jan 25, 2020
65e3ea2
block_inline: properly update imm.target of the copied instruction
leonid-s-usov Jan 25, 2020
32de6a0
WIP: coroutines++: runs but doesn't work
leonid-s-usov Jan 26, 2020
4f867be
VM: improve debug trace
leonid-s-usov Jan 26, 2020
d449484
jq_handle_write: fix a jv_free bug
leonid-s-usov Jan 26, 2020
f1f29bd
coexp: ITS ALIVE!
leonid-s-usov Jan 27, 2020
5379ef2
Add back the lost `output` builtin
Jan 28, 2020
b1d67fd
block_inline: account for the case when the inlined call is a branch …
leonid-s-usov Jan 29, 2020
48b3a75
compile: account for all backtracking instructions
leonid-s-usov Jan 29, 2020
ad5405f
preliminary optimization is the root of all evil
leonid-s-usov Jan 29, 2020
9fdf955
fix I/O policy leak
nicowilliams Jan 29, 2020
9c9013b
Fix #2051 -- iterate arrays backwards in path ctx
nicowilliams Jan 29, 2020
fd5dc00
No more EOF error
nicowilliams Jan 31, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ LIBJQ_INCS = src/builtin.h src/bytecode.h src/compile.h \
src/exec_stack.h src/jq_parser.h src/jv_alloc.h src/jv_dtoa.h \
src/jv_unicode.h src/jv_utf8_tables.h src/lexer.l src/libm.h \
src/linker.h src/locfile.h src/opcode_list.h src/parser.y \
src/util.h
src/util.h src/builtin_modules.h

LIBJQ_SRC = src/builtin.c src/bytecode.c src/compile.c src/execute.c \
src/jq_test.c src/jv.c src/jv_alloc.c src/jv_aux.c \
src/jv_dtoa.c src/jv_file.c src/jv_parse.c src/jv_print.c \
src/jv_unicode.c src/linker.c src/locfile.c src/util.c \
src/decNumber/decContext.c src/decNumber/decNumber.c \
src/jv_dtoa_tsd.c \
src/jv_dtoa_tsd.c src/builtin_modules.c \
${LIBJQ_INCS}

### C build options
Expand Down Expand Up @@ -57,11 +57,11 @@ libjq_la_LIBADD = -lm
libjq_la_LDFLAGS = $(onig_LDFLAGS) -export-symbols-regex '^j[qv]_' -version-info 1:4:0

if WIN32
libjq_la_LIBADD += -lshlwapi
libjq_la_LIBADD += -lshlwapi -lbcrypt
libjq_la_LDFLAGS += -no-undefined
endif

include_HEADERS = src/jv.h src/jq.h
include_HEADERS = src/jv.h src/jq.h src/jq_plugin.h

if ENABLE_UBSAN
AM_CFLAGS += -fsanitize=undefined
Expand Down Expand Up @@ -137,6 +137,11 @@ TESTS = tests/optionaltest tests/jqtest tests/onigtest tests/shtest tests/utf8te
endif
TESTS_ENVIRONMENT = NO_VALGRIND=$(NO_VALGRIND)

lib_LTLIBRARIES += somod.la
somod_la_SOURCES = tests/modules/somod/somod.c
somod_la_CFLAGS = -DJQ_PLUGIN
somod_la_LIBADD =
somod_la_LDFLAGS = -module

### Building the manpage

Expand Down Expand Up @@ -185,6 +190,7 @@ EXTRA_DIST = $(DOC_FILES) $(man_MANS) $(TESTS) $(TEST_LOG_COMPILER) \
tests/modules/lib/jq/e/e.jq tests/modules/lib/jq/f.jq \
tests/modules/shadow1.jq tests/modules/shadow2.jq \
tests/modules/syntaxerror/syntaxerror.jq \
tests/modules/somod/somod.jq tests/modules/somod/somod.c \
tests/modules/test_bind_order.jq \
tests/modules/test_bind_order0.jq \
tests/modules/test_bind_order1.jq \
Expand Down
107 changes: 107 additions & 0 deletions README.plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
jq FFI
======

jq now has an FFI. You can create a module with C-coded builtins.

A jq module's jq source can say:

module {"cfunctions":true};

to cause a shared object or DLL of the same name to be loaded.

An alternative shared object or DLL name can be specified instead:

module {"cfunctions":"my_shared_object"};

The shared object or DLL must export a function named `jq_plugin_init`,
or else it must list an alternative name for that function it its module
meta:

module {"cfunctions":true,"plugin_init_function":"my_plugin_init"};

The module's C sources must `#define JQ_PLUGIN` and must `#include` all
of `<jv.h>`, `<jq.h>`, and `<jq_plugin.h>`.

The init function has the following prototype:

typedef int (*jq_plugin_init_f)
(int, /* jq plugin min supported ABI version */
int, /* jq plugin max supported ABI version */
struct jq_state *,
const char **, /* error string */
const char **, /* jq-coded module contents */
size_t *, /* jq-coded module contents size */
struct cfunction **, /* array of C-coded function descriptors */
size_t *);

and must return 0 on success. On failure, it should output an error
string via the fourth argument.

The plugin must check that two jq ABI integers include the
`JQ_CURRENT_ABI` that the plugin was compiled against.

The plugin can output a replacement for its jq-coded part via the fifth
and sixth arguments. If the fifth argument is not `NULL` and the sixth
is `0`, then the string output in the fifth argument must be a C string
(i.e., `NUL`-terminated).

The plugin can output C-coded functions via the seventh and eigth init
function arguments.

Each C-coded function must have one the following prototypes:

jv my_func(struct jq_state *jq, jv input);
jv my_func(struct jq_state *jq, jv input, jv arg);
jv my_func(struct jq_state *jq, jv input, jv a0, jv a1);
..
jv my_func(struct jq_state *jq, jv input, jv a0, jv a1, jv a2, jv a3, jv a4, jv a5);

and must have an entry in the `struct cfunction` array output by the
module's init function.

`struct cfunction` is:

typedef void (*cfunction_ptr)();
struct cfunction {
cfunction_ptr fptr; /* function pointer */
const char* name; /* jq function name */
int nargs; /* number of arguments; must be at least 1 */
unsigned int pure:1; /* 1 if pure, 0 if impure */
unsigned int exported:1; /* 1 if exported, 0 if not */
};


jq plugins and DLL hell
=======================

All `jv` and `jq` functions in a plugin are replaced with macros of this
form:

#define jv_fname ((*(struct jq_plugin_vtable **)jq)->jv_fname)

Here `jq` should be the `struct jq_state *` argument to the C-coded
builtin functions and the module's init function.

This means that plugins can only call `jv` and `jq` functions in contexts
where a `jq` variable is visible (and of type `struct jq_state *jq`).

Since the plugin's init function and all its C-coded functions receive a
`struct jq_state *jq` argument, a `struct jq_state *jq` should always be
available for calling `jv` and `jq` functions.

This way, plugins do not need to be linked with `libjq`. This is quite
important, as it limits DLL hell. Essentially plugins "link" with
`libjq` dynamically at run-time without having to rely on the system's
RTLD to provide that functionality.

This means that one can use the same plugin object in applications that
use different versions of `libjq`, provided that the jq ABIs of the two
`libjq` versions are sufficiently compatible (meaning, the layout of
`jv` is the same, both provide all the functions needed by the plugin,
and so on).

Depending on the platform and how `libjq` or its callers are built
(e.g., with versioned symbols or direct binding) it should even be
possible even to have two different versions of `libjq` loaded in the
*same process* and loading the same plugins, and the plugins will always
use the `libjq` functions of the version of `libjq` calling them.
6 changes: 6 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ dnl Enable building all static
AC_ARG_ENABLE([all-static],
AC_HELP_STRING([--enable-all-static], [link jq with static libraries only]))

if test "x$enable_all_static" != xyes; then
AC_FIND_FUNC([dlopen], [dl], [#include <dlfcn.h>], [0, 0])
fi

AS_IF([test "x$enable_docs" = "xyes"],[
AC_CHECK_PROGS(pipenv_cmd, pipenv)

Expand Down Expand Up @@ -129,6 +133,8 @@ AM_CONDITIONAL([ENABLE_ALL_STATIC], [test "x$enable_all_static" = xyes])

AC_FUNC_ALLOCA

AC_FIND_FUNC([getline], [c], [#define _POSIX_C_SOURCE 200809L
#include <stdio.h>], [0, 0, 0])
AC_FIND_FUNC([isatty], [c], [#include <unistd.h>], [0])
AC_FIND_FUNC([_isatty], [c], [#include <io.h>], [0])
AC_FIND_FUNC([strptime], [c], [#include <time.h>], [0, 0, 0])
Expand Down
Loading