See added testcase 19.c from a bug report. The problem is reading outside the arguments buffers, even though we aren't allowed to. The single can_read_stream variable is not enough, sometimes we need to be able to pop into outer contexts but not into arbitrarily outside contexts. The trick is to terminate argument tokens with a EOF (and not just with 0 that makes us pop contexts), and deal with that in the few places we have to, This enables some cleanups of the can_read_stream variable use.
Unified · Split
| @@ -2887,6 +2887,7 @@ static void next_nomacro_spc(void) · 2886 unmodified lines | |||
| 2887 | } else { | 2887 | } else { |
| 2888 | next_nomacro1(); | 2888 | next_nomacro1(); |
| 2889 | } | 2889 | } |
| 2890 | //printf("token = %s\n", get_tok_str(tok, &tokc)); | ||
| 2890 | } | 2891 | } |
| 2891 | 2892 | ||
| 2892 | ST_FUNC void next_nomacro(void) | 2893 | ST_FUNC void next_nomacro(void) |
| @@ -2932,7 +2933,7 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) · 39 unmodified lines | |||
| 2932 | cstr_ccat(&cstr, '\"'); | 2933 | cstr_ccat(&cstr, '\"'); |
| 2933 | st = s->d; | 2934 | st = s->d; |
| 2934 | spc = 0; | 2935 | spc = 0; |
| 2935 | while (*st) { | 2936 | while (*st >= 0) { |
| 2936 | TOK_GET(&t, &st, &cval); | 2937 | TOK_GET(&t, &st, &cval); |
| 2937 | if (t != TOK_PLCHLDR | 2938 | if (t != TOK_PLCHLDR |
| 2938 | && t != TOK_NOSUBST | 2939 | && t != TOK_NOSUBST |
| @@ -2972,7 +2973,7 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) · 33 unmodified lines | |||
| 2972 | /* special case for var arg macros : ## eats the ',' | 2973 | /* special case for var arg macros : ## eats the ',' |
| 2973 | if empty VA_ARGS variable. */ | 2974 | if empty VA_ARGS variable. */ |
| 2974 | if (t1 == TOK_PPJOIN && t0 == ',' && gnu_ext && s->type.t) { | 2975 | if (t1 == TOK_PPJOIN && t0 == ',' && gnu_ext && s->type.t) { |
| 2975 | if (*st == 0) { | 2976 | if (*st <= 0) { |
| 2976 | /* suppress ',' '##' */ | 2977 | /* suppress ',' '##' */ |
| 2977 | str.len -= 2; | 2978 | str.len -= 2; |
| 2978 | } else { | 2979 | } else { |
| @@ -2984,12 +2985,11 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) · 5 unmodified lines | |||
| 2984 | for(;;) { | 2985 | for(;;) { |
| 2985 | int t1; | 2986 | int t1; |
| 2986 | TOK_GET(&t1, &st, &cval); | 2987 | TOK_GET(&t1, &st, &cval); |
| 2987 | if (!t1) | 2988 | if (t1 <= 0) |
| 2988 | break; | 2989 | break; |
| 2989 | tok_str_add2(&str, t1, &cval); | 2990 | tok_str_add2(&str, t1, &cval); |
| 2990 | } | 2991 | } |
| 2991 | } | 2992 | } |
| 2992 | |||
| 2993 | } else { | 2993 | } else { |
| 2994 | add_var: | 2994 | add_var: |
| 2995 | /* NOTE: the stream cannot be read when macro | 2995 | /* NOTE: the stream cannot be read when macro |
| @@ -3031,7 +3031,7 @@ static int next_argstream(Sym **nested_list, int can_read_stream, TokenString *w · 35 unmodified lines | |||
| 3031 | while (is_space(t) || TOK_LINEFEED == t || TOK_PLCHLDR == t) | 3031 | while (is_space(t) || TOK_LINEFEED == t || TOK_PLCHLDR == t) |
| 3032 | tok_str_add(ws_str, t), t = *++p; | 3032 | tok_str_add(ws_str, t), t = *++p; |
| 3033 | } | 3033 | } |
| 3034 | if (t == 0 && can_read_stream) { | 3034 | if (t == 0) { |
| 3035 | end_macro(); | 3035 | end_macro(); |
| 3036 | /* also, end of scope for nested defined symbol */ | 3036 | /* also, end of scope for nested defined symbol */ |
| 3037 | sa = *nested_list; | 3037 | sa = *nested_list; |
| @@ -3197,6 +3197,7 @@ static int macro_subst_tok( · 159 unmodified lines | |||
| 3197 | if (parlevel) | 3197 | if (parlevel) |
| 3198 | expect(")"); | 3198 | expect(")"); |
| 3199 | str.len -= spc; | 3199 | str.len -= spc; |
| 3200 | tok_str_add(&str, -1); | ||
| 3200 | tok_str_add(&str, 0); | 3201 | tok_str_add(&str, 0); |
| 3201 | sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0); | 3202 | sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0); |
| 3202 | sa1->d = str.str; | 3203 | sa1->d = str.str; |
| @@ -3366,7 +3367,7 @@ static void macro_subst( · 163 unmodified lines | |||
| 3366 | 3367 | ||
| 3367 | while (1) { | 3368 | while (1) { |
| 3368 | TOK_GET(&t, &ptr, &cval); | 3369 | TOK_GET(&t, &ptr, &cval); |
| 3369 | if (t == 0) | 3370 | if (t <= 0) |
| 3370 | break; | 3371 | break; |
| 3371 | 3372 | ||
| 3372 | if (t >= TOK_IDENT && 0 == nosubst) { | 3373 | if (t >= TOK_IDENT && 0 == nosubst) { |
| @@ -3703,6 +3704,7 @@ static int pp_need_space(int a, int b) · 330 unmodified lines | |||
| 3703 | : '+' == a ? TOK_INC == b || '+' == b | 3704 | : '+' == a ? TOK_INC == b || '+' == b |
| 3704 | : '-' == a ? TOK_DEC == b || '-' == b | 3705 | : '-' == a ? TOK_DEC == b || '-' == b |
| 3705 | : a >= TOK_IDENT ? b >= TOK_IDENT | 3706 | : a >= TOK_IDENT ? b >= TOK_IDENT |
| 3707 | : a == TOK_PPNUM ? b >= TOK_IDENT | ||
| 3706 | : 0; | 3708 | : 0; |
| 3707 | } | 3709 | } |
| 3708 | 3710 | ||
| @@ -0,0 +1,101 @@ | |||
| 1 | #define M_C2I(a, ...) a ## __VA_ARGS__ | ||
| 2 | #define M_C(a, ...) M_C2I(a, __VA_ARGS__) | ||
| 3 | #define M_C3I(a, b, ...) a ## b ## __VA_ARGS__ | ||
| 4 | #define M_C3(a, b, ...) M_C3I(a ,b, __VA_ARGS__) | ||
| 5 | |||
| 6 | #define M_RETI_ARG2(a, b, ...) b | ||
| 7 | #define M_RET_ARG2(...) M_RETI_ARG2(__VA_ARGS__) | ||
| 8 | #define M_RETI_ARG27(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,aa, ...) aa | ||
| 9 | #define M_RET_ARG27(...) M_RETI_ARG27(__VA_ARGS__) | ||
| 10 | |||
| 11 | #define M_TOBOOLI_0 1, 0, | ||
| 12 | #define M_BOOL(x) M_RET_ARG2(M_C(M_TOBOOLI_, x), 1, useless) | ||
| 13 | |||
| 14 | #define M_IFI_0(true_macro, ...) __VA_ARGS__ | ||
| 15 | #define M_IFI_1(true_macro, ...) true_macro | ||
| 16 | #define M_IF(c) M_C(M_IFI_, M_BOOL(c)) | ||
| 17 | |||
| 18 | #define M_FLAT(...) __VA_ARGS__ | ||
| 19 | #define M_INVI_0 1 | ||
| 20 | #define M_INVI_1 0 | ||
| 21 | #define M_INV(x) M_C(M_INVI_, x) | ||
| 22 | |||
| 23 | #define M_ANDI_00 0 | ||
| 24 | #define M_ANDI_01 0 | ||
| 25 | #define M_ANDI_10 0 | ||
| 26 | #define M_ANDI_11 1 | ||
| 27 | #define M_AND(x,y) M_C3(M_ANDI_, x, y) | ||
| 28 | |||
| 29 | #define M_ORI_00 0 | ||
| 30 | #define M_ORI_01 1 | ||
| 31 | #define M_ORI_10 1 | ||
| 32 | #define M_ORI_11 1 | ||
| 33 | #define M_OR(x,y) M_C3(M_ORI_, x, y) | ||
| 34 | |||
| 35 | #define M_COMMA_P(...) M_RET_ARG27(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, useless) | ||
| 36 | |||
| 37 | #define M_EMPTYI_DETECT(...) 0, 1, | ||
| 38 | #define M_EMPTYI_P_C1(...) M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__ ()) | ||
| 39 | #define M_EMPTYI_P_C2(...) M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__) | ||
| 40 | #define M_EMPTYI_P_C3(...) M_COMMA_P(__VA_ARGS__ () ) | ||
| 41 | #define M_EMPTY_P(...) M_AND(M_EMPTYI_P_C1(__VA_ARGS__), M_INV(M_OR(M_OR(M_EMPTYI_P_C2(__VA_ARGS__), M_COMMA_P(__VA_ARGS__)),M_EMPTYI_P_C3(__VA_ARGS__)))) | ||
| 42 | #define M_APPLY_FUNC2B(func, arg1, arg2) \ | ||
| 43 | M_IF(M_EMPTY_P(arg2))(,func(arg1, arg2)) | ||
| 44 | #define M_MAP2B_0(func, data, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,...) \ | ||
| 45 | M_APPLY_FUNC2B(func, data, a) M_APPLY_FUNC2B(func, data, b) M_APPLY_FUNC2B(func, data, c) \ | ||
| 46 | M_APPLY_FUNC2B(func, data, d) M_APPLY_FUNC2B(func, data, e) M_APPLY_FUNC2B(func, data, f) \ | ||
| 47 | M_APPLY_FUNC2B(func, data, g) M_APPLY_FUNC2B(func, data, h) M_APPLY_FUNC2B(func, data, i) \ | ||
| 48 | M_APPLY_FUNC2B(func, data, j) M_APPLY_FUNC2B(func, data, k) M_APPLY_FUNC2B(func, data, l) \ | ||
| 49 | M_APPLY_FUNC2B(func, data, m) M_APPLY_FUNC2B(func, data, n) M_APPLY_FUNC2B(func, data, o) \ | ||
| 50 | M_APPLY_FUNC2B(func, data, p) M_APPLY_FUNC2B(func, data, q) M_APPLY_FUNC2B(func, data, r) \ | ||
| 51 | M_APPLY_FUNC2B(func, data, s) M_APPLY_FUNC2B(func, data, t) M_APPLY_FUNC2B(func, data, u) \ | ||
| 52 | M_APPLY_FUNC2B(func, data, v) M_APPLY_FUNC2B(func, data, w) M_APPLY_FUNC2B(func, data, x) \ | ||
| 53 | M_APPLY_FUNC2B(func, data, y) M_APPLY_FUNC2B(func, data, z) | ||
| 54 | #define M_MAP2B(f, ...) M_MAP2B_0(f, __VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ) | ||
| 55 | #define M_INIT_INIT(a) ,a, | ||
| 56 | |||
| 57 | #define M_GET_METHOD(method, method_default, ...) \ | ||
| 58 | M_RET_ARG2 (M_MAP2B(M_C, M_C3(M_, method, _), __VA_ARGS__), method_default,) | ||
| 59 | |||
| 60 | #define M_TEST_METHOD_P(method, oplist) \ | ||
| 61 | M_BOOL(M_GET_METHOD (method, 0, M_FLAT oplist)) | ||
| 62 | |||
| 63 | #define TRUE 1 | ||
| 64 | #define TEST1(n) \ | ||
| 65 | M_IF(n)(ok,nok) | ||
| 66 | #define TEST2(op) \ | ||
| 67 | M_TEST_METHOD_P(INIT, op) | ||
| 68 | #define TEST3(op) \ | ||
| 69 | M_IF(M_TEST_METHOD_P(INIT, op))(ok, nok) | ||
| 70 | #define TEST4(op) \ | ||
| 71 | TEST1(TEST2(op)) | ||
| 72 | #define KO(a) ((void)1) | ||
| 73 | |||
| 74 | /* This checks that the various expansions that ultimately lead to | ||
| 75 | something like 'KO(arg,arg)', where 'KO' comes from a macro | ||
| 76 | expansion reducing from a large macro chain do not are regarded | ||
| 77 | as funclike macro invocation of KO. E.g. X93 and X94 expand to 'KO', | ||
| 78 | but X95 must not consume the (a,b) arguments outside the M_IF() | ||
| 79 | invocation to reduce the 'KO' macro to an invocation. Instead | ||
| 80 | X95 should reduce via M_IF(KO)(a,b) to 'a'. | ||
| 81 | |||
| 82 | The other lines here are variations on this scheme, with X1 to | ||
| 83 | X6 coming from the bug report at | ||
| 84 | http://lists.nongnu.org/archive/html/tinycc-devel/2017-07/msg00017.html */ | ||
| 85 | X92 M_IF(KO) | ||
| 86 | X93 M_GET_METHOD(INIT, 0, INIT(KO)) | ||
| 87 | X94 M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO))) | ||
| 88 | X95 M_IF(M_GET_METHOD(INIT, 0, INIT(KO)))(a,b) | ||
| 89 | X96 M_IF(M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO)))) | ||
| 90 | X97 M_IF(M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO))))(ok,nok) | ||
| 91 | X98 (M_TEST_METHOD_P(INIT, (INIT(KO))))(ok, nok) | ||
| 92 | X99 M_IF(M_TEST_METHOD_P(INIT, (INIT(KO))))(ok, nok) | ||
| 93 | // test begins | ||
| 94 | X1 TEST1(TRUE) // ==> expect ok, get ok | ||
| 95 | // First test with a token which is not a macro | ||
| 96 | X2 TEST2((INIT(ok))) // ==> expect 1, get 1 | ||
| 97 | X3 TEST3((INIT(ok))) // ==> expect ok, get ok | ||
| 98 | // Then test with a token which is a macro, but should not be expanded. | ||
| 99 | X4 TEST2((INIT(KO))) // ==> expect 1, get 1 | ||
| 100 | X5 TEST4(INIT(KO)) | ||
| 101 | X6 TEST3((INIT(KO))) // ==> expect ok, get "error: macro 'KO' used with too many args" | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | X92 M_IFI_1 | ||
| 2 | X93 KO | ||
| 3 | X94 KO | ||
| 4 | X95 a | ||
| 5 | X96 M_IFI_1 | ||
| 6 | X97 ok | ||
| 7 | X98 (1)(ok, nok) | ||
| 8 | X99 ok | ||
| 9 | X1 ok | ||
| 10 | X2 1 | ||
| 11 | X3 ok | ||
| 12 | X4 1 | ||
| 13 | X5 nok | ||
| 14 | X6 ok | ||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Various things I encountered while hacking the pre processor */ | ||
| 2 | #define wrap(x) x | ||
| 3 | #define pr_warning(fmt, ...) printk(KERN_WARNING fmt, ##__VA_ARGS__) | ||
| 4 | #define pr_warn(x,y) pr_warning(x,y) | ||
| 5 | #define net_ratelimited_function(function, ...) function(__VA_ARGS__) | ||
| 6 | X1 net_ratelimited_function(pr_warn, "pipapo", bla); | ||
| 7 | X2 net_ratelimited_function(wrap(pr_warn), "bla", foo); | ||
| 8 | #define two m n | ||
| 9 | #define chain4(a,b,c,d) a ## b ## c ## d | ||
| 10 | X2 chain4(two,o,p,q) | ||
| 11 | X3 chain4(o,two,p,q) | ||
| 12 | X4 chain4(o,p,two,q) | ||
| 13 | X5 chain4(o,p,q,two) | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | X1 printk(KERN_WARNING "pipapo",bla); | ||
| 2 | X2 printk(KERN_WARNING "bla",foo); | ||
| 3 | X2 twoopq | ||
| 4 | X3 otwopq | ||
| 5 | X4 optwoq | ||
| 6 | X5 opqtwo | ||
| @@ -31,10 +31,7 @@ FILTER = 2>&1 | sed 's,$(SRC)/,,g' · 30 unmodified lines | |||
| 31 | 31 | ||
| 32 | # automatically generate .expect files with gcc: | 32 | # automatically generate .expect files with gcc: |
| 33 | %.expect: # %.c | 33 | %.expect: # %.c |
| 34 | gcc -E -P $< >$*.expect 2>&1 | 34 | gcc -E -P $*.[cS] >$*.expect 2>&1 |
| 35 | |||
| 36 | %.expect: # %.S | ||
| 37 | gcc -E -P $< >$*.expect 2>&1 | ||
| 38 | 35 | ||
| 39 | # tell make not to delete | 36 | # tell make not to delete |
| 40 | .PRECIOUS: %.expect | 37 | .PRECIOUS: %.expect |