From f38f760a1129030b3a4c5c3a3fe49409a65327c2 Mon Sep 17 00:00:00 2001 From: Lukas Mai Date: Fri, 29 Mar 2024 07:52:02 +0100 Subject: [PATCH] h2ph: define all symbols at runtime Preprocessor directives must be processed strictly in order. `#if` and `#ifdef` directives can inspect the current state of defined symbols. That's why it is wrong to translate `#define FOO() ...` to `sub foo() { ... }` since subroutine definitions are processed unconditionally at compile time, before the rest of the code starts running. In particular, unless(defined(&FOO)) { sub FOO () { eval q(1); } } is equivalent to # at compile time: sub FOO () { eval q(1); } # ... later, at runtime: unless(defined(&FOO)) { # does nothing } Fix this case by always wrapping subroutines in eval '...', which moves the symbol definition to runtime, regardless of what $t (our indentation state) is. Similarly, generate `_h2ph_pre.ph` without the functionally useless `unless (defined &...) { }` blocks. We don't need runtime definitions (via eval) here because nothing in this file depends on the dynamic state of macro definitions. It's all `#define`s, no `#if`s. Fixes #22109. --- t/lib/h2ph.h | 9 +++++++++ t/lib/h2ph.pht | 14 +++++++++----- utils/h2ph.PL | 27 ++++++++------------------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/t/lib/h2ph.h b/t/lib/h2ph.h index 9897bf2c07..27abe56c6a 100644 --- a/t/lib/h2ph.h +++ b/t/lib/h2ph.h @@ -16,6 +16,15 @@ #define ERROR(x) fprintf(stderr, "%s\n", x[2][3][0]) #endif /* ERROR */ +/* check for correct order of definitions vs. conditionals */ +#ifdef NOT_DEFINED_HERE() + /* handle indented directives */ + #error "NOT_DEFINED_HERE should not be defined at this point!" +#endif + +/* function-like macro with no parameters, outside of any conditional */ +#define NOT_DEFINED_HERE() 42 + #ifndef _H2PH_H_ #define _H2PH_H_ diff --git a/t/lib/h2ph.pht b/t/lib/h2ph.pht index f068d6dae4..1276d6a8c3 100644 --- a/t/lib/h2ph.pht +++ b/t/lib/h2ph.pht @@ -2,18 +2,22 @@ require '_h2ph_pre.ph'; no warnings qw(redefine misc); -unless(defined(&SQUARE)) { - sub SQUARE { - my($x) = @_; +eval 'sub SQUARE { + my($x) = @_; eval q((($x)*($x))); - } -} +}' unless defined(&SQUARE); unless(defined(&ERROR)) { eval 'sub ERROR { my($x) = @_; eval q( &fprintf( &stderr, \\"%s\\\\n\\", $x->[2][3][0])); }' unless defined(&ERROR); } +if(defined(&NOT_DEFINED_HERE)) { + die("NOT_DEFINED_HERE should not be defined at this point!"); +} +eval 'sub NOT_DEFINED_HERE () { + eval q(42); +}' unless defined(&NOT_DEFINED_HERE); unless(defined(&_H2PH_H_)) { eval 'sub _H2PH_H_ () {1;}' unless defined(&_H2PH_H_); # "$Revision h2ph.h,v 1.0 98/05/04 20:42:14 billy $" diff --git a/utils/h2ph.PL b/utils/h2ph.PL index afa53c2dba..7985cad3ce 100644 --- a/utils/h2ph.PL +++ b/utils/h2ph.PL @@ -392,7 +392,6 @@ sub EMIT { $new = reindent($new); $args = reindent($args); - if ($t ne '') { $new =~ s/(['\\])/\\$1/g; #']); if ($opt_h) { print OUT $t, @@ -402,9 +401,6 @@ sub EMIT { print OUT $t, "eval 'sub $name $proto\{\n$t ${args}eval q($new);\n$t}' unless defined(\&$name);\n"; } - } else { - print OUT "unless(defined(\&$name)) {\n sub $name $proto\{\n\t${args}eval q($new);\n }\n}\n"; - } %curargs = (); return; } @@ -774,7 +770,7 @@ sub inc_dirs sub build_preamble_if_necessary { # Increment $VERSION every time this function is modified: - my $VERSION = 4; + my $VERSION = 5; my $preamble = "$Dest_dir/_h2ph_pre.ph"; # Can we skip building the preamble file? @@ -812,18 +808,16 @@ sub build_preamble_if_necessary my $def = $define{$_}; $def =~ s/$arg/\$\{$arg\}/g; print PREAMBLE < 10; - print PREAMBLE - "unless (defined &$_) { sub $_() { $code } }\n\n"; + print PREAMBLE "sub $_() { $code }\n\n"; } elsif ($define{$_} =~ /^\w+$/) { my $def = $define{$_}; if ($isatype{$def}) { - print PREAMBLE - "unless (defined &$_) { sub $_() { \"$def\" } }\n\n"; + print PREAMBLE "sub $_() { \"$def\" }\n\n"; } else { - print PREAMBLE - "unless (defined &$_) { sub $_() { &$def } }\n\n"; + print PREAMBLE "sub $_() { &$def }\n\n"; } } else { - print PREAMBLE - "unless (defined &$_) { sub $_() { \"", - quotemeta($define{$_}), "\" } }\n\n"; + print PREAMBLE "sub $_() { \"\Q$define{$_}\E\" }\n\n"; } } print PREAMBLE "\n1;\n"; # avoid 'did not return a true value' when empty