Skip to content

Commit 72a2a0c

Browse files
committed
builtin.c: typecheck builtin cfunctions in function_list
In C23 (default C standard used by GCC 15), jv (*fptr)(); has become equivalent to jv (*fptr)(void); so we can no longer assign builtin implemenations directly to the fptr member of cfunctions without generating a compile error. Since there does not seem to be any straight-forward way to tell autoconf to force the compiler to use C99 short of explicitly adding -std=c99 to CFLAGS, it is probably a cleaner solution to just make the code C23 compatible. A possible solution could have been to just redeclare cfunction.fptr as void*, but then the functions' return type would not have been type checked (e.g. if you tried to add a {printf, "printf", 2}, where printf is a function that does not return jv, the compiler wouldn't have complained.) We were already not typechecking the arguments of the functions, so e.g. {binop_plus, "_plus", 3}, /* instead of {f_plus, "_plus, 3}, */ {f_setpath, "setpath", 4}, /* instead of {f_setpath, "setpath", 3}, */ compile without errors despite not having the correct prototype. So I thought of instead improving the situation by redefining cfunction.fptr as a union of function pointers with the prototypes that the jq bytecode interpreter can call, and use a macro to add the builtin functions to function_list using to the arity argument to assign the implementation function to the appropriate union member. Now the code won't compile if the wrong arity, or an arity not supported by the bytecode interpreter (>5 = 1input+4arguments), or a prototype not jallable by the bytecode interpreter (e.g. binop_plus that doesn't expect a jq_state* argument). Also, the code now compiles with gcc -std=c23. Fixes #3206
1 parent 96e8d89 commit 72a2a0c

File tree

3 files changed

+80
-71
lines changed

3 files changed

+80
-71
lines changed

src/builtin.c

Lines changed: 67 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,85 +1790,88 @@ static jv f_have_decnum(jq_state *jq, jv a) {
17901790
#endif
17911791
}
17921792

1793+
#define CFUNC(func, name, arity) \
1794+
{.fptr = { .a ## arity = func }, name, arity}
1795+
17931796
#define LIBM_DD(name) \
1794-
{f_ ## name, #name, 1},
1797+
CFUNC(f_ ## name, #name, 1),
17951798
#define LIBM_DD_NO(name) LIBM_DD(name)
17961799
#define LIBM_DA(name, type) LIBM_DD(name)
17971800
#define LIBM_DA_NO(name, type) LIBM_DD(name)
17981801

17991802
#define LIBM_DDD(name) \
1800-
{f_ ## name, #name, 3},
1803+
CFUNC(f_ ## name, #name, 3),
18011804
#define LIBM_DDD_NO(name) LIBM_DDD(name)
18021805

18031806
#define LIBM_DDDD(name) \
1804-
{f_ ## name, #name, 4},
1807+
CFUNC(f_ ## name, #name, 4),
18051808
#define LIBM_DDDD_NO(name) LIBM_DDDD(name)
18061809

18071810
static const struct cfunction function_list[] = {
18081811
#include "libm.h"
1809-
{f_negate, "_negate", 1},
1810-
#define BINOP(name) {f_ ## name, "_" #name, 3},
1812+
CFUNC(f_negate, "_negate", 1),
1813+
#define BINOP(name) CFUNC(f_ ## name, "_" #name, 3),
18111814
BINOPS
18121815
#undef BINOP
1813-
{f_dump, "tojson", 1},
1814-
{f_json_parse, "fromjson", 1},
1815-
{f_tonumber, "tonumber", 1},
1816-
{f_tostring, "tostring", 1},
1817-
{f_keys, "keys", 1},
1818-
{f_keys_unsorted, "keys_unsorted", 1},
1819-
{f_startswith, "startswith", 2},
1820-
{f_endswith, "endswith", 2},
1821-
{f_string_split, "split", 2},
1822-
{f_string_explode, "explode", 1},
1823-
{f_string_implode, "implode", 1},
1824-
{f_string_indexes, "_strindices", 2},
1825-
{f_string_trim, "trim", 1},
1826-
{f_string_ltrim, "ltrim", 1},
1827-
{f_string_rtrim, "rtrim", 1},
1828-
{f_setpath, "setpath", 3}, // FIXME typechecking
1829-
{f_getpath, "getpath", 2},
1830-
{f_delpaths, "delpaths", 2},
1831-
{f_has, "has", 2},
1832-
{f_contains, "contains", 2},
1833-
{f_length, "length", 1},
1834-
{f_utf8bytelength, "utf8bytelength", 1},
1835-
{f_type, "type", 1},
1836-
{f_isinfinite, "isinfinite", 1},
1837-
{f_isnan, "isnan", 1},
1838-
{f_isnormal, "isnormal", 1},
1839-
{f_infinite, "infinite", 1},
1840-
{f_nan, "nan", 1},
1841-
{f_sort, "sort", 1},
1842-
{f_sort_by_impl, "_sort_by_impl", 2},
1843-
{f_group_by_impl, "_group_by_impl", 2},
1844-
{f_min, "min", 1},
1845-
{f_max, "max", 1},
1846-
{f_min_by_impl, "_min_by_impl", 2},
1847-
{f_max_by_impl, "_max_by_impl", 2},
1848-
{f_error, "error", 1},
1849-
{f_format, "format", 2},
1850-
{f_env, "env", 1},
1851-
{f_halt, "halt", 1},
1852-
{f_halt_error, "halt_error", 2},
1853-
{f_get_search_list, "get_search_list", 1},
1854-
{f_get_prog_origin, "get_prog_origin", 1},
1855-
{f_get_jq_origin, "get_jq_origin", 1},
1856-
{f_match, "_match_impl", 4},
1857-
{f_modulemeta, "modulemeta", 1},
1858-
{f_input, "input", 1},
1859-
{f_debug, "debug", 1},
1860-
{f_stderr, "stderr", 1},
1861-
{f_strptime, "strptime", 2},
1862-
{f_strftime, "strftime", 2},
1863-
{f_strflocaltime, "strflocaltime", 2},
1864-
{f_mktime, "mktime", 1},
1865-
{f_gmtime, "gmtime", 1},
1866-
{f_localtime, "localtime", 1},
1867-
{f_now, "now", 1},
1868-
{f_current_filename, "input_filename", 1},
1869-
{f_current_line, "input_line_number", 1},
1870-
{f_have_decnum, "have_decnum", 1},
1871-
{f_have_decnum, "have_literal_numbers", 1},
1816+
CFUNC(f_dump, "tojson", 1),
1817+
CFUNC(f_json_parse, "fromjson", 1),
1818+
CFUNC(f_tonumber, "tonumber", 1),
1819+
CFUNC(f_tostring, "tostring", 1),
1820+
CFUNC(f_keys, "keys", 1),
1821+
CFUNC(f_keys_unsorted, "keys_unsorted", 1),
1822+
CFUNC(f_startswith, "startswith", 2),
1823+
CFUNC(f_endswith, "endswith", 2),
1824+
CFUNC(f_string_split, "split", 2),
1825+
CFUNC(f_string_explode, "explode", 1),
1826+
CFUNC(f_string_implode, "implode", 1),
1827+
CFUNC(f_string_indexes, "_strindices", 2),
1828+
CFUNC(f_string_trim, "trim", 1),
1829+
CFUNC(f_string_ltrim, "ltrim", 1),
1830+
CFUNC(f_string_rtrim, "rtrim", 1),
1831+
CFUNC(f_setpath, "setpath", 3),
1832+
CFUNC(f_getpath, "getpath", 2),
1833+
CFUNC(f_delpaths, "delpaths", 2),
1834+
CFUNC(f_has, "has", 2),
1835+
CFUNC(f_contains, "contains", 2),
1836+
CFUNC(f_length, "length", 1),
1837+
CFUNC(f_utf8bytelength, "utf8bytelength", 1),
1838+
CFUNC(f_type, "type", 1),
1839+
CFUNC(f_isinfinite, "isinfinite", 1),
1840+
CFUNC(f_isnan, "isnan", 1),
1841+
CFUNC(f_isnormal, "isnormal", 1),
1842+
CFUNC(f_infinite, "infinite", 1),
1843+
CFUNC(f_nan, "nan", 1),
1844+
CFUNC(f_sort, "sort", 1),
1845+
CFUNC(f_sort_by_impl, "_sort_by_impl", 2),
1846+
CFUNC(f_group_by_impl, "_group_by_impl", 2),
1847+
CFUNC(f_min, "min", 1),
1848+
CFUNC(f_max, "max", 1),
1849+
CFUNC(f_min_by_impl, "_min_by_impl", 2),
1850+
CFUNC(f_max_by_impl, "_max_by_impl", 2),
1851+
CFUNC(f_error, "error", 1),
1852+
CFUNC(f_format, "format", 2),
1853+
CFUNC(f_env, "env", 1),
1854+
CFUNC(f_halt, "halt", 1),
1855+
CFUNC(f_halt_error, "halt_error", 2),
1856+
CFUNC(f_get_search_list, "get_search_list", 1),
1857+
CFUNC(f_get_prog_origin, "get_prog_origin", 1),
1858+
CFUNC(f_get_jq_origin, "get_jq_origin", 1),
1859+
CFUNC(f_match, "_match_impl", 4),
1860+
CFUNC(f_modulemeta, "modulemeta", 1),
1861+
CFUNC(f_input, "input", 1),
1862+
CFUNC(f_debug, "debug", 1),
1863+
CFUNC(f_stderr, "stderr", 1),
1864+
CFUNC(f_strptime, "strptime", 2),
1865+
CFUNC(f_strftime, "strftime", 2),
1866+
CFUNC(f_strflocaltime, "strflocaltime", 2),
1867+
CFUNC(f_mktime, "mktime", 1),
1868+
CFUNC(f_gmtime, "gmtime", 1),
1869+
CFUNC(f_localtime, "localtime", 1),
1870+
CFUNC(f_now, "now", 1),
1871+
CFUNC(f_current_filename, "input_filename", 1),
1872+
CFUNC(f_current_line, "input_line_number", 1),
1873+
CFUNC(f_have_decnum, "have_decnum", 1),
1874+
CFUNC(f_have_decnum, "have_literal_numbers", 1),
18721875
};
18731876
#undef LIBM_DDDD_NO
18741877
#undef LIBM_DDD_NO

src/bytecode.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#define BYTECODE_H
33
#include <stdint.h>
44

5-
#include "jv.h"
5+
#include "jq.h"
66

77
typedef enum {
88
#define OP(name, imm, in, out) name,
@@ -46,7 +46,13 @@ const struct opcode_description* opcode_describe(opcode op);
4646

4747
#define MAX_CFUNCTION_ARGS 10
4848
struct cfunction {
49-
jv (*fptr)();
49+
union {
50+
jv (*a1)(jq_state *, jv);
51+
jv (*a2)(jq_state *, jv, jv);
52+
jv (*a3)(jq_state *, jv, jv, jv);
53+
jv (*a4)(jq_state *, jv, jv, jv, jv);
54+
jv (*a5)(jq_state *, jv, jv, jv, jv, jv);
55+
} fptr;
5056
const char* name;
5157
int nargs;
5258
};

src/execute.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -917,11 +917,11 @@ jv jq_next(jq_state *jq) {
917917
}
918918
struct cfunction* function = &frame_current(jq)->bc->globals->cfunctions[*pc++];
919919
switch (function->nargs) {
920-
case 1: top = ((jv (*)(jq_state *, jv))function->fptr)(jq, in[0]); break;
921-
case 2: top = ((jv (*)(jq_state *, jv, jv))function->fptr)(jq, in[0], in[1]); break;
922-
case 3: top = ((jv (*)(jq_state *, jv, jv, jv))function->fptr)(jq, in[0], in[1], in[2]); break;
923-
case 4: top = ((jv (*)(jq_state *, jv, jv, jv, jv))function->fptr)(jq, in[0], in[1], in[2], in[3]); break;
924-
case 5: top = ((jv (*)(jq_state *, jv, jv, jv, jv, jv))function->fptr)(jq, in[0], in[1], in[2], in[3], in[4]); break;
920+
case 1: top = function->fptr.a1(jq, in[0]); break;
921+
case 2: top = function->fptr.a2(jq, in[0], in[1]); break;
922+
case 3: top = function->fptr.a3(jq, in[0], in[1], in[2]); break;
923+
case 4: top = function->fptr.a4(jq, in[0], in[1], in[2], in[3]); break;
924+
case 5: top = function->fptr.a5(jq, in[0], in[1], in[2], in[3], in[4]); break;
925925
// FIXME: a) up to 7 arguments (input + 6), b) should assert
926926
// because the compiler should not generate this error.
927927
default: return jv_invalid_with_msg(jv_string("Function takes too many arguments"));

0 commit comments

Comments
 (0)