[perl #74006] 5.12.0-RC stuffing bug
Zefram [Wed, 14 Apr 2010 07:29:15 +0000 (09:29 +0200)]
There's a small bug in lex_stuff_pvn() that causes spurious syntax errors
in an obscure situation.  It happens if stuffing is performed on the
last line of a file, and the line ends with a statement that lacks its
terminating semicolon.  Attached patch fixes and adds test.

MANIFEST
ext/XS-APItest-KeywordRPN/KeywordRPN.xs
ext/XS-APItest-KeywordRPN/t/stuff_svcur_bug.t [new file with mode: 0644]
toke.c

index 6363cc8..da0c1f3 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -3218,6 +3218,7 @@ ext/XS-APItest-KeywordRPN/Makefile.PL     XS::APItest::KeywordRPN extension
 ext/XS-APItest-KeywordRPN/README       XS::APItest::KeywordRPN extension
 ext/XS-APItest-KeywordRPN/t/keyword_plugin.t   test keyword plugin mechanism
 ext/XS-APItest-KeywordRPN/t/multiline.t        test plugin parsing across lines
+ext/XS-APItest-KeywordRPN/t/stuff_svcur_bug.t  test for a bug in lex_stuff_pvn
 ext/XS-APItest/Makefile.PL     XS::APItest extension
 ext/XS-APItest/MANIFEST                XS::APItest extension
 ext/XS-APItest/notcore.c       Test API functions when PERL_CORE is not defined
index e205eea..adb7e6b 100644 (file)
@@ -8,7 +8,7 @@
        (!sv_is_glob(sv) && !sv_is_regexp(sv) && \
         (SvFLAGS(sv) & (SVf_IOK|SVf_NOK|SVf_POK|SVp_IOK|SVp_NOK|SVp_POK)))
 
-static SV *hintkey_rpn_sv, *hintkey_calcrpn_sv;
+static SV *hintkey_rpn_sv, *hintkey_calcrpn_sv, *hintkey_stufftest_sv;
 static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
 
 /* low-level parser helpers */
@@ -150,6 +150,27 @@ static OP *THX_parse_keyword_calcrpn(pTHX)
 }
 #define parse_keyword_calcrpn() THX_parse_keyword_calcrpn(aTHX)
 
+static OP *THX_parse_keyword_stufftest(pTHX)
+{
+       I32 c;
+       bool do_stuff;
+       lex_read_space(0);
+       do_stuff = lex_peek_unichar(0) == '+';
+       if(do_stuff) {
+               lex_read_unichar(0);
+               lex_read_space(0);
+       }
+       c = lex_peek_unichar(0);
+       if(c == ';') {
+               lex_read_unichar(0);
+       } else if(c != /*{*/'}') {
+               croak("syntax error");
+       }
+       if(do_stuff) lex_stuff_pvn(" ", 1, 0);
+       return newOP(OP_NULL, 0);
+}
+#define parse_keyword_stufftest() THX_parse_keyword_stufftest(aTHX)
+
 /* plugin glue */
 
 static int THX_keyword_active(pTHX_ SV *hintkey_sv)
@@ -200,6 +221,10 @@ static int my_keyword_plugin(pTHX_
                        keyword_active(hintkey_calcrpn_sv)) {
                *op_ptr = parse_keyword_calcrpn();
                return KEYWORD_PLUGIN_STMT;
+       } else if(keyword_len == 9 && strnEQ(keyword_ptr, "stufftest", 9) &&
+                       keyword_active(hintkey_stufftest_sv)) {
+               *op_ptr = parse_keyword_stufftest();
+               return KEYWORD_PLUGIN_STMT;
        } else {
                return next_keyword_plugin(aTHX_
                                keyword_ptr, keyword_len, op_ptr);
@@ -211,6 +236,8 @@ MODULE = XS::APItest::KeywordRPN PACKAGE = XS::APItest::KeywordRPN
 BOOT:
        hintkey_rpn_sv = newSVpvs_share("XS::APItest::KeywordRPN/rpn");
        hintkey_calcrpn_sv = newSVpvs_share("XS::APItest::KeywordRPN/calcrpn");
+       hintkey_stufftest_sv =
+               newSVpvs_share("XS::APItest::KeywordRPN/stufftest");
        next_keyword_plugin = PL_keyword_plugin;
        PL_keyword_plugin = my_keyword_plugin;
 
@@ -225,6 +252,9 @@ PPCODE:
                        keyword_enable(hintkey_rpn_sv);
                } else if(sv_is_string(item) && strEQ(SvPVX(item), "calcrpn")) {
                        keyword_enable(hintkey_calcrpn_sv);
+               } else if(sv_is_string(item) &&
+                               strEQ(SvPVX(item), "stufftest")) {
+                       keyword_enable(hintkey_stufftest_sv);
                } else {
                        croak("\"%s\" is not exported by the %s module",
                                SvPV_nolen(item), SvPV_nolen(ST(0)));
@@ -242,6 +272,9 @@ PPCODE:
                        keyword_disable(hintkey_rpn_sv);
                } else if(sv_is_string(item) && strEQ(SvPVX(item), "calcrpn")) {
                        keyword_disable(hintkey_calcrpn_sv);
+               } else if(sv_is_string(item) &&
+                               strEQ(SvPVX(item), "stufftest")) {
+                       keyword_disable(hintkey_stufftest_sv);
                } else {
                        croak("\"%s\" is not exported by the %s module",
                                SvPV_nolen(item), SvPV_nolen(ST(0)));
diff --git a/ext/XS-APItest-KeywordRPN/t/stuff_svcur_bug.t b/ext/XS-APItest-KeywordRPN/t/stuff_svcur_bug.t
new file mode 100644 (file)
index 0000000..4fd6e11
--- /dev/null
@@ -0,0 +1,12 @@
+use warnings;
+use strict;
+
+use Test::More tests => 1;
+ok 1;
+
+use XS::APItest::KeywordRPN qw(stufftest);
+
+# In the buggy case, a syntax error occurs at EOF.
+# Adding a semicolon, any following statements, or anything else
+# causes the bug not to show itself.
+stufftest+;()
diff --git a/toke.c b/toke.c
index 19fce67..b6735cf 100644 (file)
--- a/toke.c
+++ b/toke.c
@@ -956,6 +956,8 @@ Perl_lex_stuff_pvn(pTHX_ char *pv, STRLEN len, U32 flags)
            lex_grow_linestr(SvCUR(PL_parser->linestr)+1+len+highhalf);
            bufptr = PL_parser->bufptr;
            Move(bufptr, bufptr+len+highhalf, PL_parser->bufend+1-bufptr, char);
+           SvCUR_set(PL_parser->linestr,
+               SvCUR(PL_parser->linestr) + len+highhalf);
            PL_parser->bufend += len+highhalf;
            for (p = pv; p != e; p++) {
                U8 c = (U8)*p;
@@ -994,6 +996,8 @@ Perl_lex_stuff_pvn(pTHX_ char *pv, STRLEN len, U32 flags)
            lex_grow_linestr(SvCUR(PL_parser->linestr)+1+len-highhalf);
            bufptr = PL_parser->bufptr;
            Move(bufptr, bufptr+len-highhalf, PL_parser->bufend+1-bufptr, char);
+           SvCUR_set(PL_parser->linestr,
+               SvCUR(PL_parser->linestr) + len-highhalf);
            PL_parser->bufend += len-highhalf;
            for (p = pv; p != e; p++) {
                U8 c = (U8)*p;
@@ -1009,6 +1013,7 @@ Perl_lex_stuff_pvn(pTHX_ char *pv, STRLEN len, U32 flags)
            lex_grow_linestr(SvCUR(PL_parser->linestr)+1+len);
            bufptr = PL_parser->bufptr;
            Move(bufptr, bufptr+len, PL_parser->bufend+1-bufptr, char);
+           SvCUR_set(PL_parser->linestr, SvCUR(PL_parser->linestr) + len);
            PL_parser->bufend += len;
            Copy(pv, bufptr, len, char);
        }