X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=Declare.xs;h=2c715e28a2fe09dbc4cc55c7ea0c9d8dcd3b8b6d;hb=f1b89adc9d1738287606d2c7c5fcde06b1fcba53;hp=78177fbd1984209d088831c6c8fd690e84786590;hpb=9603b8dc4de630701f2c9c60087a99ab28045044;p=p5sagit%2FDevel-Declare.git diff --git a/Declare.xs b/Declare.xs index 78177fb..2c715e2 100644 --- a/Declare.xs +++ b/Declare.xs @@ -1,17 +1,33 @@ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" +#include "hook_op_check.h" #undef printf #include "stolen_chunk_of_toke.c" #include #include +#define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s) +#define PERL_DECIMAL_VERSION \ + PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION) +#define PERL_VERSION_GE(r,v,s) \ + (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s)) + #ifndef Newx # define Newx(v,n,t) New(0,v,n,t) #endif /* !Newx */ +#define DD_DEBUGf_UPDATED_LINESTR 1 +#define DD_DEBUGf_TRACE 2 + +#define DD_DEBUG_UPDATED_LINESTR (dd_debug & DD_DEBUGf_UPDATED_LINESTR) +#define DD_DEBUG_TRACE (dd_debug & DD_DEBUGf_TRACE) static int dd_debug = 0; +#define DD_CONST_VIA_RV2CV PERL_VERSION_GE(5,11,2) + +#define DD_GROW_VIA_BLOCKHOOK PERL_VERSION_GE(5,13,3) + #define LEX_NORMAL 10 #define LEX_INTERPNORMAL 9 @@ -25,7 +41,7 @@ static int in_declare = 0; #define DD_AM_LEXING_CHECK (PL_lex_state == LEX_NORMAL || PL_lex_state == LEX_INTERPNORMAL) -#ifdef PL_parser +#if defined(PL_parser) || defined(PERL_5_9_PLUS) #define DD_HAVE_PARSER PL_parser #define DD_HAVE_LEX_STUFF (PL_parser && PL_lex_stuff) #define DD_AM_LEXING (PL_parser && DD_AM_LEXING_CHECK) @@ -52,7 +68,7 @@ int dd_is_declarator(pTHX_ char* name) { /* $declarators{$current_package_name} */ if (!HvNAME(PL_curstash)) - return -1; + return -1; is_declarator_pack_ref = hv_fetch(is_declarator, HvNAME(PL_curstash), strlen(HvNAME(PL_curstash)), FALSE); @@ -72,7 +88,7 @@ int dd_is_declarator(pTHX_ char* name) { /* requires SvIOK as well as TRUE since flags not being an int is useless */ if (!is_declarator_flag_ref - || !SvIOK(*is_declarator_flag_ref) + || !SvIOK(*is_declarator_flag_ref) || !SvTRUE(*is_declarator_flag_ref)) return -1; @@ -113,19 +129,31 @@ char* dd_get_linestr(pTHX) { } void dd_set_linestr(pTHX_ char* new_value) { - int new_len = strlen(new_value); + unsigned int new_len = strlen(new_value); if (SvLEN(PL_linestr) < new_len) { - croak("forced to realloc PL_linestr for line %s, bailing out before we crash harder", SvPVX(PL_linestr)); + croak("PL_linestr not long enough, was Devel::Declare loaded soon enough in %s", + CopFILE(&PL_compiling) + ); } - SvGROW(PL_linestr, new_len); memcpy(SvPVX(PL_linestr), new_value, new_len+1); SvCUR_set(PL_linestr, new_len); PL_bufend = SvPVX(PL_linestr) + new_len; + + if ( DD_DEBUG_UPDATED_LINESTR && PERLDB_LINE && PL_curstash != PL_debstash) { + // Cribbed from toke.c + SV * const sv = NEWSV(85,0); + + sv_upgrade(sv, SVt_PVMG); + sv_setpvn(sv,PL_bufptr,PL_bufend-PL_bufptr); + (void)SvIOK_on(sv); + SvIV_set(sv, 0); + av_store(CopFILEAV(&PL_compiling),(I32)CopLINE(&PL_compiling),sv); + } } char* dd_get_lex_stuff(pTHX) { @@ -178,56 +206,79 @@ int dd_toke_scan_word(pTHX_ int offset, int handle_package) { return s - base_s; } +int dd_toke_scan_ident(pTHX_ int offset) { + char tmpbuf[sizeof PL_tokenbuf]; + char* base_s = SvPVX(PL_linestr) + offset; + char* s = scan_ident(base_s, PL_bufend, tmpbuf, sizeof tmpbuf, 0); + return s - base_s; +} + int dd_toke_scan_str(pTHX_ int offset) { + STRLEN remaining = sv_len(PL_linestr) - offset; + SV* line_copy = newSVsv(PL_linestr); char* base_s = SvPVX(PL_linestr) + offset; char* s = scan_str(base_s, FALSE, FALSE); + if (s != base_s && sv_len(PL_lex_stuff) > remaining) { + int ret = (s - SvPVX(PL_linestr)) + remaining; + sv_catsv(line_copy, PL_linestr); + dd_set_linestr(aTHX_ SvPV_nolen(line_copy)); + SvREFCNT_dec(line_copy); + return ret; + } return s - base_s; } int dd_toke_skipspace(pTHX_ int offset) { char* base_s = SvPVX(PL_linestr) + offset; - char* s = skipspace(base_s); + char* s = skipspace_force(base_s); return s - base_s; } -/* replacement PL_check rv2cv entry */ +static void call_done_declare(pTHX) { + dSP; + + if (DD_DEBUG_TRACE) { + printf("Deconstructing declare\n"); + printf("PL_bufptr: %s\n", PL_bufptr); + printf("bufend at: %i\n", PL_bufend - PL_bufptr); + printf("linestr: %s\n", SvPVX(PL_linestr)); + printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr)); + } + + ENTER; + SAVETMPS; + + PUSHMARK(SP); -STATIC OP *(*dd_old_ck_rv2cv)(pTHX_ OP *op); + call_pv("Devel::Declare::done_declare", G_VOID|G_DISCARD); -STATIC OP *dd_ck_rv2cv(pTHX_ OP *o) { + FREETMPS; + LEAVE; + + if (DD_DEBUG_TRACE) { + printf("PL_bufptr: %s\n", PL_bufptr); + printf("bufend at: %i\n", PL_bufend - PL_bufptr); + printf("linestr: %s\n", SvPVX(PL_linestr)); + printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr)); + printf("actual len: %i\n", strlen(PL_bufptr)); + } +} + +static int dd_handle_const(pTHX_ char *name); + +/* replacement PL_check rv2cv entry */ + +STATIC OP *dd_ck_rv2cv(pTHX_ OP *o, void *user_data) { OP* kid; int dd_flags; - o = dd_old_ck_rv2cv(aTHX_ o); /* let the original do its job */ + PERL_UNUSED_VAR(user_data); - if (in_declare) { - if (dd_debug) { - printf("Deconstructing declare\n"); - printf("PL_bufptr: %s\n", PL_bufptr); - printf("bufend at: %i\n", PL_bufend - PL_bufptr); - printf("linestr: %s\n", SvPVX(PL_linestr)); - printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr)); - } + if (!DD_AM_LEXING) + return o; /* not lexing? */ - dSP; - - ENTER; - SAVETMPS; - - PUSHMARK(SP); - - call_pv("Devel::Declare::done_declare", G_VOID|G_DISCARD); - - FREETMPS; - LEAVE; - - if (dd_debug) { - printf("PL_bufptr: %s\n", PL_bufptr); - printf("bufend at: %i\n", PL_bufend - PL_bufptr); - printf("linestr: %s\n", SvPVX(PL_linestr)); - printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr)); - printf("actual len: %i\n", strlen(PL_bufptr)); - } + if (in_declare) { + call_done_declare(aTHX); return o; } @@ -236,10 +287,7 @@ STATIC OP *dd_ck_rv2cv(pTHX_ OP *o) { if (kid->op_type != OP_GV) /* not a GV so ignore */ return o; - if (!DD_AM_LEXING) - return o; /* not lexing? */ - - if (dd_debug) { + if (DD_DEBUG_TRACE) { printf("Checking GV %s -> %s\n", HvNAME(GvSTASH(kGVOP_gv)), GvNAME(kGVOP_gv)); } @@ -248,25 +296,59 @@ STATIC OP *dd_ck_rv2cv(pTHX_ OP *o) { if (dd_flags == -1) return o; - if (dd_debug) { + if (DD_DEBUG_TRACE) { printf("dd_flags are: %i\n", dd_flags); printf("PL_tokenbuf: %s\n", PL_tokenbuf); } +#if DD_CONST_VIA_RV2CV + if (PL_expect != XOPERATOR) { + if (!dd_handle_const(aTHX_ GvNAME(kGVOP_gv))) + return o; + CopLINE(PL_curcop) = PL_copline; + /* The parser behaviour that we're simulating depends on what comes + after the declarator. */ + if (*skipspace(PL_bufptr + strlen(GvNAME(kGVOP_gv))) != '(') { + if (in_declare) { + call_done_declare(aTHX); + } else { + dd_linestr_callback(aTHX_ "rv2cv", GvNAME(kGVOP_gv)); + } + } + return o; + } +#endif /* DD_CONST_VIA_RV2CV */ + dd_linestr_callback(aTHX_ "rv2cv", GvNAME(kGVOP_gv)); return o; } -STATIC OP *(*dd_old_ck_entereval)(pTHX_ OP *op); +#if DD_GROW_VIA_BLOCKHOOK + +static void dd_block_start(pTHX_ int full) +{ + PERL_UNUSED_VAR(full); + if (SvLEN(PL_linestr) < 8192) + (void) lex_grow_linestr(8192); +} + +#else /* !DD_GROW_VIA_BLOCKHOOK */ OP* dd_pp_entereval(pTHX) { dSP; - dPOPss; STRLEN len; const char* s; + SV *sv; +#ifdef PERL_5_9_PLUS + SV *saved_hh; + if (PL_op->op_private & OPpEVAL_HAS_HH) { + saved_hh = POPs; + } +#endif + sv = POPs; if (SvPOK(sv)) { - if (dd_debug) { + if (DD_DEBUG_TRACE) { printf("mangling eval sv\n"); } if (SvREADONLY(sv)) @@ -281,16 +363,24 @@ OP* dd_pp_entereval(pTHX) { SvGROW(sv, 8192); } PUSHs(sv); +#ifdef PERL_5_9_PLUS + if (PL_op->op_private & OPpEVAL_HAS_HH) { + PUSHs(saved_hh); + } +#endif return PL_ppaddr[OP_ENTEREVAL](aTHX); } -STATIC OP *dd_ck_entereval(pTHX_ OP *o) { - o = dd_old_ck_entereval(aTHX_ o); /* let the original do its job */ +STATIC OP *dd_ck_entereval(pTHX_ OP *o, void *user_data) { + PERL_UNUSED_VAR(user_data); + if (o->op_ppaddr == PL_ppaddr[OP_ENTEREVAL]) o->op_ppaddr = dd_pp_entereval; return o; } +#endif /* !DD_GROW_VIA_BLOCKHOOK */ + static I32 dd_filter_realloc(pTHX_ int idx, SV *sv, int maxlen) { const I32 count = FILTER_READ(idx+1, sv, maxlen); @@ -299,13 +389,66 @@ static I32 dd_filter_realloc(pTHX_ int idx, SV *sv, int maxlen) return count; } -STATIC OP *(*dd_old_ck_const)(pTHX_ OP*op); +static int dd_handle_const(pTHX_ char *name) { + switch (PL_lex_inwhat) { + case OP_QR: + case OP_MATCH: + case OP_SUBST: + case OP_TRANS: + case OP_BACKTICK: + case OP_STRINGIFY: + return 0; + break; + default: + break; + } + + if (strnEQ(PL_bufptr, "->", 2)) { + return 0; + } + + { + char buf[256]; + STRLEN len; + char *s = PL_bufptr; + STRLEN old_offset = PL_bufptr - SvPVX(PL_linestr); + + s = scan_word(s, buf, sizeof buf, FALSE, &len); + if (strnEQ(buf, name, len)) { + char *d; + SV *inject = newSVpvn(SvPVX(PL_linestr), PL_bufptr - SvPVX(PL_linestr)); + sv_catpvn(inject, buf, len); + + d = peekspace(s); + sv_catpvn(inject, s, d - s); + + if ((PL_bufend - d) >= 2 && strnEQ(d, "=>", 2)) { + return 0; + } + + sv_catpv(inject, d); + dd_set_linestr(aTHX_ SvPV_nolen(inject)); + PL_bufptr = SvPVX(PL_linestr) + old_offset; + SvREFCNT_dec (inject); + } + } + + dd_linestr_callback(aTHX_ "const", name); + + return 1; +} + +#if !DD_CONST_VIA_RV2CV -STATIC OP *dd_ck_const(pTHX_ OP *o) { +STATIC OP *dd_ck_const(pTHX_ OP *o, void *user_data) { int dd_flags; char* name; - o = dd_old_ck_const(aTHX_ o); /* let the original do its job */ + PERL_UNUSED_VAR(user_data); + + if (DD_HAVE_PARSER && PL_expect == XOPERATOR) { + return o; + } /* if this is set, we just grabbed a delimited string or something, not a bareword, so NO TOUCHY */ @@ -324,11 +467,13 @@ STATIC OP *dd_ck_const(pTHX_ OP *o) { if (dd_flags == -1) return o; - dd_linestr_callback(aTHX_ "const", name); + dd_handle_const(aTHX_ name); - return o; + return o; } +#endif /* !DD_CONST_VIA_RV2CV */ + static int initialized = 0; MODULE = Devel::Declare PACKAGE = Devel::Declare @@ -339,12 +484,21 @@ void setup() CODE: if (!initialized++) { - dd_old_ck_rv2cv = PL_check[OP_RV2CV]; - PL_check[OP_RV2CV] = dd_ck_rv2cv; - dd_old_ck_entereval = PL_check[OP_ENTEREVAL]; - PL_check[OP_ENTEREVAL] = dd_ck_entereval; - dd_old_ck_const = PL_check[OP_CONST]; - PL_check[OP_CONST] = dd_ck_const; +#if DD_GROW_VIA_BLOCKHOOK + static BHK bhk; +#if PERL_VERSION_GE(5,13,6) + BhkENTRY_set(&bhk, bhk_start, dd_block_start); +#else /* <5.13.6 */ + BhkENTRY_set(&bhk, start, dd_block_start); +#endif /* <5.13.6 */ + Perl_blockhook_register(aTHX_ &bhk); +#else /* !DD_GROW_VIA_BLOCKHOOK */ + hook_op_check(OP_ENTEREVAL, dd_ck_entereval, NULL); +#endif /* !DD_GROW_VIA_BLOCKHOOK */ + hook_op_check(OP_RV2CV, dd_ck_rv2cv, NULL); +#if !DD_CONST_VIA_RV2CV + hook_op_check(OP_CONST, dd_ck_const, NULL); +#endif /* !DD_CONST_VIA_RV2CV */ } filter_add(dd_filter_realloc, NULL); @@ -408,6 +562,13 @@ toke_scan_str(int offset); RETVAL int +toke_scan_ident(int offset) + CODE: + RETVAL = dd_toke_scan_ident(aTHX_ offset); + OUTPUT: + RETVAL + +int toke_skipspace(int offset) CODE: RETVAL = dd_toke_skipspace(aTHX_ offset); @@ -427,6 +588,13 @@ set_in_declare(int value) in_declare = value; BOOT: - if (getenv ("DD_DEBUG")) { - dd_debug = 1; +{ + char *endptr; + char *debug_str = getenv ("DD_DEBUG"); + if (debug_str) { + dd_debug = strtol (debug_str, &endptr, 10); + if (*endptr != '\0') { + dd_debug = 0; + } } +}