X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=Declare.xs;h=edfbc0df664179824140eadc9c12ec61efb4a4fa;hb=6d6f4cad27fa7e1f88d781d842e09e21d4c0cff4;hp=4eda199795fb0a4441b3ae80aeb230c6cf0b61c2;hpb=a9fb5fb190e5623ea3abea5b59aa646b2065d533;p=p5sagit%2FDevel-Declare.git diff --git a/Declare.xs b/Declare.xs index 4eda199..edfbc0d 100644 --- a/Declare.xs +++ b/Declare.xs @@ -1,8 +1,7 @@ -#define PERL_CORE -#define PERL_NO_GET_CONTEXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" +#include "hook_op_check.h" #undef printf #include "stolen_chunk_of_toke.c" #include @@ -12,23 +11,7 @@ # define Newx(v,n,t) New(0,v,n,t) #endif /* !Newx */ -#if 1 -#define DD_HAS_TRAITS -#endif - -#if 0 -#define DD_DEBUG -#endif - -#define DD_HANDLE_NAME 1 -#define DD_HANDLE_PROTO 2 -#define DD_HANDLE_PACKAGE 8 - -#ifdef DD_DEBUG -#define DD_DEBUG_S printf("Buffer: %s\n", s); -#else -#define DD_DEBUG_S -#endif +static int dd_debug = 0; #define LEX_NORMAL 10 #define LEX_INTERPNORMAL 9 @@ -37,6 +20,22 @@ static int in_declare = 0; +/* in 5.10, PL_parser will be NULL if we aren't parsing, and PL_lex_stuff + is a lookup into it - so if anything else we can use to tell, so we + need to be a bit more careful if PL_parser exists */ + +#define DD_AM_LEXING_CHECK (PL_lex_state == LEX_NORMAL || PL_lex_state == LEX_INTERPNORMAL) + +#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) +#else +#define DD_HAVE_PARSER 1 +#define DD_HAVE_LEX_STUFF PL_lex_stuff +#define DD_AM_LEXING DD_AM_LEXING_CHECK +#endif + /* thing that decides whether we're dealing with a declarator */ int dd_is_declarator(pTHX_ char* name) { @@ -51,6 +50,11 @@ int dd_is_declarator(pTHX_ char* name) { if (!is_declarator) return -1; + /* $declarators{$current_package_name} */ + + if (!HvNAME(PL_curstash)) + return -1; + is_declarator_pack_ref = hv_fetch(is_declarator, HvNAME(PL_curstash), strlen(HvNAME(PL_curstash)), FALSE); @@ -59,6 +63,8 @@ int dd_is_declarator(pTHX_ char* name) { is_declarator_pack_hash = (HV*) SvRV(*is_declarator_pack_ref); + /* $declarators{$current_package_name}{$name} */ + is_declarator_flag_ref = hv_fetch( is_declarator_pack_hash, name, strlen(name), FALSE @@ -67,7 +73,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; @@ -76,78 +82,76 @@ int dd_is_declarator(pTHX_ char* name) { return dd_flags; } -/* replacement PL_check rv2cv entry */ +/* callback thingy */ -STATIC OP *(*dd_old_ck_rv2cv)(pTHX_ OP *op); +void dd_linestr_callback (pTHX_ char* type, char* name) { -STATIC OP *dd_ck_rv2cv(pTHX_ OP *o) { - OP* kid; - char* s; - char* save_s; - char tmpbuf[sizeof PL_tokenbuf]; - char found_name[sizeof PL_tokenbuf]; - char* found_proto = NULL, *found_traits = NULL; - STRLEN len = 0; - int dd_flags; - char* cb_args[6]; - dSP; /* define stack pointer for later call stuff */ - char* retstr; - STRLEN n_a; /* for POPpx */ + char* linestr = SvPVX(PL_linestr); + int offset = PL_bufptr - linestr; - o = dd_old_ck_rv2cv(aTHX_ o); /* let the original do its job */ + dSP; - if (in_declare) { - cb_args[0] = NULL; -#ifdef 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)); -#endif - call_argv("Devel::Declare::done_declare", G_VOID|G_DISCARD, cb_args); - in_declare--; -#ifdef 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)); -#endif - return o; + ENTER; + SAVETMPS; + + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(type, 0))); + XPUSHs(sv_2mortal(newSVpv(name, 0))); + XPUSHs(sv_2mortal(newSViv(offset))); + PUTBACK; + + call_pv("Devel::Declare::linestr_callback", G_VOID|G_DISCARD); + + FREETMPS; + LEAVE; +} + +char* dd_get_linestr(pTHX) { + if (!DD_HAVE_PARSER) { + return NULL; } + return SvPVX(PL_linestr); +} - kid = cUNOPo->op_first; +void dd_set_linestr(pTHX_ char* new_value) { + unsigned int new_len = strlen(new_value); - if (kid->op_type != OP_GV) /* not a GV so ignore */ - return o; + if (SvLEN(PL_linestr) < new_len) { + croak("forced to realloc PL_linestr for line %s, bailing out before we crash harder", SvPVX(PL_linestr)); + } - if (PL_lex_state != LEX_NORMAL && PL_lex_state != LEX_INTERPNORMAL) - return o; /* not lexing? */ + SvGROW(PL_linestr, new_len); - /* I was doing this, but the CONST wrap can't so it didn't gain anything - stash = GvSTASH(kGVOP_gv); */ + memcpy(SvPVX(PL_linestr), new_value, new_len+1); -#ifdef DD_DEBUG - printf("Checking GV %s -> %s\n", HvNAME(GvSTASH(kGVOP_gv)), GvNAME(kGVOP_gv)); -#endif + SvCUR_set(PL_linestr, new_len); - dd_flags = dd_is_declarator(aTHX_ GvNAME(kGVOP_gv)); + PL_bufend = SvPVX(PL_linestr) + new_len; +} - if (dd_flags == -1) - return o; +char* dd_get_lex_stuff(pTHX) { + return (DD_HAVE_LEX_STUFF ? SvPVX(PL_lex_stuff) : ""); +} -#ifdef DD_DEBUG - printf("dd_flags are: %i\n", dd_flags); -#endif +void dd_clear_lex_stuff(pTHX) { + if (DD_HAVE_PARSER) + PL_lex_stuff = (SV*)NULL; +} - s = PL_bufptr; /* copy the current buffer pointer */ +char* dd_get_curstash_name(pTHX) { + return HvNAME(PL_curstash); +} - DD_DEBUG_S +int dd_get_linestr_offset(pTHX) { + char* linestr; + if (!DD_HAVE_PARSER) { + return -1; + } + linestr = SvPVX(PL_linestr); + return PL_bufptr - linestr; +} -#ifdef DD_DEBUG - printf("PL_tokenbuf: %s\n", PL_tokenbuf); -#endif +char* dd_move_past_token (pTHX_ char* s) { /* * buffer will be at the beginning of the declarator, -unless- the @@ -158,155 +162,132 @@ STATIC OP *dd_ck_rv2cv(pTHX_ OP *o) { while (s < PL_bufend && isSPACE(*s)) s++; if (memEQ(s, PL_tokenbuf, strlen(PL_tokenbuf))) s += strlen(PL_tokenbuf); + return s; +} + +int dd_toke_move_past_token (pTHX_ int offset) { + char* base_s = SvPVX(PL_linestr) + offset; + char* s = dd_move_past_token(aTHX_ base_s); + return s - base_s; +} + +int dd_toke_scan_word(pTHX_ int offset, int handle_package) { + char tmpbuf[sizeof PL_tokenbuf]; + char* base_s = SvPVX(PL_linestr) + offset; + STRLEN len; + char* s = scan_word(base_s, tmpbuf, sizeof tmpbuf, handle_package, &len); + 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; +} - DD_DEBUG_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; +} - if (dd_flags & DD_HANDLE_NAME) { +int dd_toke_skipspace(pTHX_ int offset) { + char* base_s = SvPVX(PL_linestr) + offset; + char* s = skipspace(base_s); + return s - base_s; +} - /* find next word */ +/* replacement PL_check rv2cv entry */ - s = skipspace(s); +STATIC OP *dd_ck_rv2cv(pTHX_ OP *o, void *user_data) { + dSP; + OP* kid; + int dd_flags; - DD_DEBUG_S + PERL_UNUSED_VAR(user_data); - /* kill the :: added in the ck_const */ - if (*s == ':') - *s++ = ' '; - if (*s == ':') - *s++ = ' '; + 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)); + } - /* arg 4 is allow_package */ + ENTER; + SAVETMPS; - s = scan_word(s, tmpbuf, sizeof tmpbuf, dd_flags & DD_HANDLE_PACKAGE, &len); + PUSHMARK(SP); - DD_DEBUG_S + call_pv("Devel::Declare::done_declare", G_VOID|G_DISCARD); - if (len) { - strcpy(found_name, tmpbuf); -#ifdef DD_DEBUG - printf("Found %s\n", found_name); -#endif + 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)); } + return o; } - if (dd_flags & DD_HANDLE_PROTO) { - - s = skipspace(s); - - if (*s == '(') { /* found a prototype-ish thing */ - save_s = s; - s = scan_str(s, FALSE, FALSE); /* no keep_quoted, no keep_delims */ -#ifdef DD_HAS_TRAITS - { - char *traitstart = s = skipspace(s); - - while (*s && *s != '{') ++s; - if (*s) { - int tlen = s - traitstart; - Newx(found_traits, tlen+1, char); - Copy(traitstart, found_traits, tlen, char); - found_traits[tlen] = 0; -#ifdef DD_DEBUG - printf("found traits..... (%s)\n", found_traits); -#endif - } - } -#endif - - if (SvPOK(PL_lex_stuff)) { -#ifdef DD_DEBUG - printf("Found proto %s\n", SvPVX(PL_lex_stuff)); -#endif - found_proto = SvPVX(PL_lex_stuff); - if (len) /* foo name () => foo name X, only foo parsed so works */ - *save_s++ = ' '; - else /* foo () => foo =X, TOKEN('&') won't handle foo X */ - *save_s++ = '='; - *save_s++ = 'X'; - while (save_s < s) { - *save_s++ = ' '; - } -#ifdef DD_DEBUG - printf("Curbuf %s\n", PL_bufptr); -#endif - } - } + kid = cUNOPo->op_first; + + if (kid->op_type != OP_GV) /* not a GV so ignore */ + return o; + + if (!DD_AM_LEXING) + return o; /* not lexing? */ + + if (dd_debug) { + printf("Checking GV %s -> %s\n", HvNAME(GvSTASH(kGVOP_gv)), GvNAME(kGVOP_gv)); } - if (!len) - found_name[0] = 0; + dd_flags = dd_is_declarator(aTHX_ GvNAME(kGVOP_gv)); -#ifdef DD_DEBUG - printf("Calling init_declare\n"); -#endif - cb_args[0] = HvNAME(PL_curstash); - cb_args[1] = GvNAME(kGVOP_gv); - cb_args[2] = HvNAME(PL_curstash); - cb_args[3] = found_name; - cb_args[4] = found_proto; - cb_args[5] = found_traits; - cb_args[6] = NULL; - - if (len && found_proto) - in_declare = 2; - else if (len || found_proto) - in_declare = 1; - if (found_proto) - PL_lex_stuff = Nullsv; - s = skipspace(s); -#ifdef DD_DEBUG - printf("cur buf: %s\n", s); - printf("bufend at: %i\n", PL_bufend - s); - printf("linestr: %s\n", SvPVX(PL_linestr)); - printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr)); -#endif - - if (*s++ == '{') { - call_argv("Devel::Declare::init_declare", G_SCALAR, cb_args); - SPAGAIN; - retstr = POPpx; - PUTBACK; - if (retstr && strlen(retstr)) { - const char* old_start = SvPVX(PL_linestr); - int start_diff; - const int old_len = SvCUR(PL_linestr); -#ifdef DD_DEBUG - printf("Got string %s\n", retstr); -#endif - SvGROW(PL_linestr, (STRLEN)(old_len + strlen(retstr))); - if (start_diff = SvPVX(PL_linestr) - old_start) { - Perl_croak(aTHX_ "forced to realloc PL_linestr for line %s, bailing out before we crash harder", SvPVX(PL_linestr)); - } - memmove(s+strlen(retstr), s, (PL_bufend - s)+1); - memmove(s, retstr, strlen(retstr)); - SvCUR_set(PL_linestr, old_len + strlen(retstr)); - PL_bufend += strlen(retstr); -#ifdef DD_DEBUG - printf("cur buf: %s\n", s); - printf("PL_bufptr: %s\n", PL_bufptr); - printf("bufend at: %i\n", PL_bufend - s); - printf("linestr: %s\n", SvPVX(PL_linestr)); - printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr)); - printf("tokenbuf now: %s\n", PL_tokenbuf); -#endif - } - } else { - call_argv("Devel::Declare::init_declare", G_VOID|G_DISCARD, cb_args); + if (dd_flags == -1) + return o; + + if (dd_debug) { + printf("dd_flags are: %i\n", dd_flags); + printf("PL_tokenbuf: %s\n", PL_tokenbuf); } + + dd_linestr_callback(aTHX_ "rv2cv", GvNAME(kGVOP_gv)); + return o; } -STATIC OP *(*dd_old_ck_entereval)(pTHX_ OP *op); - OP* dd_pp_entereval(pTHX) { dSP; - dPOPss; STRLEN len; const char* s; - if (SvPOK(sv)) { -#ifdef DD_DEBUG - printf("mangling eval sv\n"); + 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) { + printf("mangling eval sv\n"); + } if (SvREADONLY(sv)) sv = sv_2mortal(newSVsv(sv)); s = SvPVX(sv); @@ -319,11 +300,17 @@ 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; @@ -337,94 +324,153 @@ static I32 dd_filter_realloc(pTHX_ int idx, SV *sv, int maxlen) return count; } -STATIC OP *(*dd_old_ck_const)(pTHX_ OP*op); - -STATIC OP *dd_ck_const(pTHX_ OP *o) { +STATIC OP *dd_ck_const(pTHX_ OP *o, void *user_data) { int dd_flags; - char* s; - char tmpbuf[sizeof PL_tokenbuf]; - char found_name[sizeof PL_tokenbuf]; - STRLEN len = 0; + char* name; + + PERL_UNUSED_VAR(user_data); - o = dd_old_ck_const(aTHX_ o); /* let the original do its job */ + 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 */ + + if (DD_HAVE_LEX_STUFF) + return o; /* don't try and look this up if it's not a string const */ if (!SvPOK(cSVOPo->op_sv)) return o; - dd_flags = dd_is_declarator(aTHX_ SvPVX(cSVOPo->op_sv)); + name = SvPVX(cSVOPo->op_sv); + + dd_flags = dd_is_declarator(aTHX_ name); if (dd_flags == -1) return o; - if (!(dd_flags & DD_HANDLE_NAME)) - return o; /* if we're not handling name, method intuiting not an issue */ + switch (PL_lex_inwhat) { + case OP_QR: + case OP_MATCH: + case OP_SUBST: + case OP_TRANS: + case OP_BACKTICK: + case OP_STRINGIFY: + return o; + break; + default: + break; + } -#ifdef DD_DEBUG - printf("Think I found a declarator %s\n", PL_tokenbuf); - printf("linestr: %s\n", SvPVX(PL_linestr)); -#endif + dd_linestr_callback(aTHX_ "const", name); - s = PL_bufptr; + return o; +} - while (s < PL_bufend && isSPACE(*s)) s++; - if (memEQ(s, PL_tokenbuf, strlen(PL_tokenbuf))) - s += strlen(PL_tokenbuf); +static int initialized = 0; - DD_DEBUG_S +MODULE = Devel::Declare PACKAGE = Devel::Declare - /* find next word */ +PROTOTYPES: DISABLE - s = skipspace(s); +void +setup() + CODE: + if (!initialized++) { + hook_op_check(OP_RV2CV, dd_ck_rv2cv, NULL); + hook_op_check(OP_ENTEREVAL, dd_ck_entereval, NULL); + hook_op_check(OP_CONST, dd_ck_const, NULL); + } + filter_add(dd_filter_realloc, NULL); - DD_DEBUG_S +char* +get_linestr() + CODE: + RETVAL = dd_get_linestr(aTHX); + OUTPUT: + RETVAL - /* arg 4 is allow_package */ +void +set_linestr(char* new_value) + CODE: + dd_set_linestr(aTHX_ new_value); - s = scan_word(s, tmpbuf, sizeof tmpbuf, dd_flags & DD_HANDLE_PACKAGE, &len); +char* +get_lex_stuff() + CODE: + RETVAL = dd_get_lex_stuff(aTHX); + OUTPUT: + RETVAL - DD_DEBUG_S +void +clear_lex_stuff() + CODE: + dd_clear_lex_stuff(aTHX); - if (len) { - const char* old_start = SvPVX(PL_linestr); - int start_diff; - const int old_len = SvCUR(PL_linestr); +char* +get_curstash_name() + CODE: + RETVAL = dd_get_curstash_name(aTHX); + OUTPUT: + RETVAL - strcpy(found_name, tmpbuf); -#ifdef DD_DEBUG - printf("Found %s\n", found_name); -#endif +int +get_linestr_offset() + CODE: + RETVAL = dd_get_linestr_offset(aTHX); + OUTPUT: + RETVAL - s -= len; - SvGROW(PL_linestr, (STRLEN)(old_len + 2)); - if (start_diff = SvPVX(PL_linestr) - old_start) { - Perl_croak(aTHX_ "forced to realloc PL_linestr for line %s, bailing out before we crash harder", SvPVX(PL_linestr)); - } - memmove(s+2, s, (PL_bufend - s)+1); - *s = ':'; - s++; - *s = ':'; - SvCUR_set(PL_linestr, old_len + 2); - PL_bufend += 2; - } - return o; -} +int +toke_scan_word(int offset, int handle_package) + CODE: + RETVAL = dd_toke_scan_word(aTHX_ offset, handle_package); + OUTPUT: + RETVAL -static int initialized = 0; +int +toke_move_past_token(int offset); + CODE: + RETVAL = dd_toke_move_past_token(aTHX_ offset); + OUTPUT: + RETVAL -MODULE = Devel::Declare PACKAGE = Devel::Declare +int +toke_scan_str(int offset); + CODE: + RETVAL = dd_toke_scan_str(aTHX_ offset); + OUTPUT: + RETVAL -PROTOTYPES: DISABLE +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); + OUTPUT: + RETVAL + +int +get_in_declare() + CODE: + RETVAL = in_declare; + OUTPUT: + RETVAL void -setup() +set_in_declare(int value) 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; + in_declare = value; + +BOOT: + if (getenv ("DD_DEBUG")) { + dd_debug = 1; } - filter_add(dd_filter_realloc, NULL);