Re-alloc PL_linestr in block hooks, if available
[p5sagit/Devel-Declare.git] / Declare.xs
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4 #include "hook_op_check.h"
5 #undef printf
6 #include "stolen_chunk_of_toke.c"
7 #include <stdio.h>
8 #include <string.h>
9
10 #define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
11 #define PERL_DECIMAL_VERSION \
12   PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
13 #define PERL_VERSION_GE(r,v,s) \
14   (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
15
16 #ifndef Newx
17 # define Newx(v,n,t) New(0,v,n,t)
18 #endif /* !Newx */
19
20 #define DD_DEBUGf_UPDATED_LINESTR 1
21 #define DD_DEBUGf_TRACE 2
22
23 #define DD_DEBUG_UPDATED_LINESTR (dd_debug & DD_DEBUGf_UPDATED_LINESTR)
24 #define DD_DEBUG_TRACE (dd_debug & DD_DEBUGf_TRACE)
25 static int dd_debug = 0;
26
27 #define DD_CONST_VIA_RV2CV PERL_VERSION_GE(5,11,2)
28
29 #define DD_GROW_VIA_BLOCKHOOK PERL_VERSION_GE(5,13,3)
30
31 #define LEX_NORMAL    10
32 #define LEX_INTERPNORMAL   9
33
34 /* flag to trigger removal of temporary declaree sub */
35
36 static int in_declare = 0;
37
38 /* in 5.10, PL_parser will be NULL if we aren't parsing, and PL_lex_stuff
39    is a lookup into it - so if anything else we can use to tell, so we
40    need to be a bit more careful if PL_parser exists */
41
42 #define DD_AM_LEXING_CHECK (PL_lex_state == LEX_NORMAL || PL_lex_state == LEX_INTERPNORMAL)
43
44 #if defined(PL_parser) || defined(PERL_5_9_PLUS)
45 #define DD_HAVE_PARSER PL_parser
46 #define DD_HAVE_LEX_STUFF (PL_parser && PL_lex_stuff)
47 #define DD_AM_LEXING (PL_parser && DD_AM_LEXING_CHECK)
48 #else
49 #define DD_HAVE_PARSER 1
50 #define DD_HAVE_LEX_STUFF PL_lex_stuff
51 #define DD_AM_LEXING DD_AM_LEXING_CHECK
52 #endif
53
54 /* thing that decides whether we're dealing with a declarator */
55
56 int dd_is_declarator(pTHX_ char* name) {
57   HV* is_declarator;
58   SV** is_declarator_pack_ref;
59   HV* is_declarator_pack_hash;
60   SV** is_declarator_flag_ref;
61   int dd_flags;
62
63   is_declarator = get_hv("Devel::Declare::declarators", FALSE);
64
65   if (!is_declarator)
66     return -1;
67
68   /* $declarators{$current_package_name} */
69
70   if (!HvNAME(PL_curstash))
71     return -1;
72
73   is_declarator_pack_ref = hv_fetch(is_declarator, HvNAME(PL_curstash),
74                              strlen(HvNAME(PL_curstash)), FALSE);
75
76   if (!is_declarator_pack_ref || !SvROK(*is_declarator_pack_ref))
77     return -1; /* not a hashref */
78
79   is_declarator_pack_hash = (HV*) SvRV(*is_declarator_pack_ref);
80
81   /* $declarators{$current_package_name}{$name} */
82
83   is_declarator_flag_ref = hv_fetch(
84     is_declarator_pack_hash, name,
85     strlen(name), FALSE
86   );
87
88   /* requires SvIOK as well as TRUE since flags not being an int is useless */
89
90   if (!is_declarator_flag_ref
91         || !SvIOK(*is_declarator_flag_ref)
92         || !SvTRUE(*is_declarator_flag_ref))
93     return -1;
94
95   dd_flags = SvIVX(*is_declarator_flag_ref);
96
97   return dd_flags;
98 }
99
100 /* callback thingy */
101
102 void dd_linestr_callback (pTHX_ char* type, char* name) {
103
104   char* linestr = SvPVX(PL_linestr);
105   int offset = PL_bufptr - linestr;
106
107   dSP;
108
109   ENTER;
110   SAVETMPS;
111
112   PUSHMARK(SP);
113   XPUSHs(sv_2mortal(newSVpv(type, 0)));
114   XPUSHs(sv_2mortal(newSVpv(name, 0)));
115   XPUSHs(sv_2mortal(newSViv(offset)));
116   PUTBACK;
117
118   call_pv("Devel::Declare::linestr_callback", G_VOID|G_DISCARD);
119
120   FREETMPS;
121   LEAVE;
122 }
123
124 char* dd_get_linestr(pTHX) {
125   if (!DD_HAVE_PARSER) {
126     return NULL;
127   }
128   return SvPVX(PL_linestr);
129 }
130
131 void dd_set_linestr(pTHX_ char* new_value) {
132   unsigned int new_len = strlen(new_value);
133
134   if (SvLEN(PL_linestr) < new_len) {
135     croak("PL_linestr not long enough, was Devel::Declare loaded soon enough in %s",
136       CopFILE(&PL_compiling)
137     );
138   }
139
140
141   memcpy(SvPVX(PL_linestr), new_value, new_len+1);
142
143   SvCUR_set(PL_linestr, new_len);
144
145   PL_bufend = SvPVX(PL_linestr) + new_len;
146
147   if ( DD_DEBUG_UPDATED_LINESTR && PERLDB_LINE && PL_curstash != PL_debstash) {
148     // Cribbed from toke.c
149     SV * const sv = NEWSV(85,0);
150
151     sv_upgrade(sv, SVt_PVMG);
152     sv_setpvn(sv,PL_bufptr,PL_bufend-PL_bufptr);
153     (void)SvIOK_on(sv);
154     SvIV_set(sv, 0);
155     av_store(CopFILEAV(&PL_compiling),(I32)CopLINE(&PL_compiling),sv);
156   }
157 }
158
159 char* dd_get_lex_stuff(pTHX) {
160   return (DD_HAVE_LEX_STUFF ? SvPVX(PL_lex_stuff) : "");
161 }
162
163 void dd_clear_lex_stuff(pTHX) {
164   if (DD_HAVE_PARSER)
165     PL_lex_stuff = (SV*)NULL;
166 }
167
168 char* dd_get_curstash_name(pTHX) {
169   return HvNAME(PL_curstash);
170 }
171
172 int dd_get_linestr_offset(pTHX) {
173   char* linestr;
174   if (!DD_HAVE_PARSER) {
175     return -1;
176   }
177   linestr = SvPVX(PL_linestr);
178   return PL_bufptr - linestr;
179 }
180
181 char* dd_move_past_token (pTHX_ char* s) {
182
183   /*
184    *   buffer will be at the beginning of the declarator, -unless- the
185    *   declarator is at EOL in which case it'll be the next useful line
186    *   so we don't short-circuit out if we don't find the declarator
187    */
188
189   while (s < PL_bufend && isSPACE(*s)) s++;
190   if (memEQ(s, PL_tokenbuf, strlen(PL_tokenbuf)))
191     s += strlen(PL_tokenbuf);
192   return s;
193 }
194
195 int dd_toke_move_past_token (pTHX_ int offset) {
196   char* base_s = SvPVX(PL_linestr) + offset;
197   char* s = dd_move_past_token(aTHX_ base_s);
198   return s - base_s;
199 }
200
201 int dd_toke_scan_word(pTHX_ int offset, int handle_package) {
202   char tmpbuf[sizeof PL_tokenbuf];
203   char* base_s = SvPVX(PL_linestr) + offset;
204   STRLEN len;
205   char* s = scan_word(base_s, tmpbuf, sizeof tmpbuf, handle_package, &len);
206   return s - base_s;
207 }
208
209 int dd_toke_scan_ident(pTHX_ int offset) {
210     char tmpbuf[sizeof PL_tokenbuf];
211     char* base_s = SvPVX(PL_linestr) + offset;
212     char* s = scan_ident(base_s, PL_bufend, tmpbuf, sizeof tmpbuf, 0);
213     return s - base_s;
214 }
215
216 int dd_toke_scan_str(pTHX_ int offset) {
217   STRLEN remaining = sv_len(PL_linestr) - offset;
218   SV* line_copy = newSVsv(PL_linestr);
219   char* base_s = SvPVX(PL_linestr) + offset;
220   char* s = scan_str(base_s, FALSE, FALSE);
221   if (s != base_s && sv_len(PL_lex_stuff) > remaining) {
222     int ret = (s - SvPVX(PL_linestr)) + remaining;
223     sv_catsv(line_copy, PL_linestr);
224     dd_set_linestr(aTHX_ SvPV_nolen(line_copy));
225     SvREFCNT_dec(line_copy);
226     return ret;
227   }
228   return s - base_s;
229 }
230
231 int dd_toke_skipspace(pTHX_ int offset) {
232   char* base_s = SvPVX(PL_linestr) + offset;
233   char* s = skipspace_force(base_s);
234   return s - base_s;
235 }
236
237 static void call_done_declare(pTHX) {
238   dSP;
239
240   if (DD_DEBUG_TRACE) {
241     printf("Deconstructing declare\n");
242     printf("PL_bufptr: %s\n", PL_bufptr);
243     printf("bufend at: %i\n", PL_bufend - PL_bufptr);
244     printf("linestr: %s\n", SvPVX(PL_linestr));
245     printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr));
246   }
247
248   ENTER;
249   SAVETMPS;
250
251   PUSHMARK(SP);
252
253   call_pv("Devel::Declare::done_declare", G_VOID|G_DISCARD);
254
255   FREETMPS;
256   LEAVE;
257
258   if (DD_DEBUG_TRACE) {
259     printf("PL_bufptr: %s\n", PL_bufptr);
260     printf("bufend at: %i\n", PL_bufend - PL_bufptr);
261     printf("linestr: %s\n", SvPVX(PL_linestr));
262     printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr));
263     printf("actual len: %i\n", strlen(PL_bufptr));
264   }
265 }
266
267 static int dd_handle_const(pTHX_ char *name);
268
269 /* replacement PL_check rv2cv entry */
270
271 STATIC OP *dd_ck_rv2cv(pTHX_ OP *o, void *user_data) {
272   OP* kid;
273   int dd_flags;
274
275   PERL_UNUSED_VAR(user_data);
276
277   if (in_declare) {
278     call_done_declare(aTHX);
279     return o;
280   }
281
282   kid = cUNOPo->op_first;
283
284   if (kid->op_type != OP_GV) /* not a GV so ignore */
285     return o;
286
287   if (!DD_AM_LEXING)
288     return o; /* not lexing? */
289
290   if (DD_DEBUG_TRACE) {
291     printf("Checking GV %s -> %s\n", HvNAME(GvSTASH(kGVOP_gv)), GvNAME(kGVOP_gv));
292   }
293
294   dd_flags = dd_is_declarator(aTHX_ GvNAME(kGVOP_gv));
295
296   if (dd_flags == -1)
297     return o;
298
299   if (DD_DEBUG_TRACE) {
300     printf("dd_flags are: %i\n", dd_flags);
301     printf("PL_tokenbuf: %s\n", PL_tokenbuf);
302   }
303
304 #if DD_CONST_VIA_RV2CV
305   if (PL_expect != XOPERATOR) {
306     if (!dd_handle_const(aTHX_ GvNAME(kGVOP_gv)))
307       return o;
308     CopLINE(PL_curcop) = PL_copline;
309     /* The parser behaviour that we're simulating depends on what comes
310        after the declarator. */
311     if (*skipspace(PL_bufptr + strlen(GvNAME(kGVOP_gv))) != '(') {
312       if (in_declare) {
313         call_done_declare(aTHX);
314       } else {
315         dd_linestr_callback(aTHX_ "rv2cv", GvNAME(kGVOP_gv));
316       }
317     }
318     return o;
319   }
320 #endif /* DD_CONST_VIA_RV2CV */
321
322   dd_linestr_callback(aTHX_ "rv2cv", GvNAME(kGVOP_gv));
323
324   return o;
325 }
326
327 #if DD_GROW_VIA_BLOCKHOOK
328
329 static void dd_block_start(pTHX_ int full)
330 {
331   PERL_UNUSED_VAR(full);
332   if (SvLEN(PL_linestr) < 8192)
333     (void) lex_grow_linestr(8192);
334 }
335
336 #else /* !DD_GROW_VIA_BLOCKHOOK */
337
338 OP* dd_pp_entereval(pTHX) {
339   dSP;
340   STRLEN len;
341   const char* s;
342   SV *sv;
343 #ifdef PERL_5_9_PLUS
344   SV *saved_hh;
345   if (PL_op->op_private & OPpEVAL_HAS_HH) {
346     saved_hh = POPs;
347   }
348 #endif
349   sv = POPs;
350   if (SvPOK(sv)) {
351     if (DD_DEBUG_TRACE) {
352       printf("mangling eval sv\n");
353     }
354     if (SvREADONLY(sv))
355       sv = sv_2mortal(newSVsv(sv));
356     s = SvPVX(sv);
357     len = SvCUR(sv);
358     if (!len || s[len-1] != ';') {
359       if (!(SvFLAGS(sv) & SVs_TEMP))
360         sv = sv_2mortal(newSVsv(sv));
361       sv_catpvn(sv, "\n;", 2);
362     }
363     SvGROW(sv, 8192);
364   }
365   PUSHs(sv);
366 #ifdef PERL_5_9_PLUS
367   if (PL_op->op_private & OPpEVAL_HAS_HH) {
368     PUSHs(saved_hh);
369   }
370 #endif
371   return PL_ppaddr[OP_ENTEREVAL](aTHX);
372 }
373
374 STATIC OP *dd_ck_entereval(pTHX_ OP *o, void *user_data) {
375   PERL_UNUSED_VAR(user_data);
376
377   if (o->op_ppaddr == PL_ppaddr[OP_ENTEREVAL])
378     o->op_ppaddr = dd_pp_entereval;
379   return o;
380 }
381
382 static I32 dd_filter_realloc(pTHX_ int idx, SV *sv, int maxlen)
383 {
384   const I32 count = FILTER_READ(idx+1, sv, maxlen);
385   SvGROW(sv, 8192); /* please try not to have a line longer than this :) */
386   /* filter_del(dd_filter_realloc); */
387   return count;
388 }
389
390 #endif /* !DD_GROW_VIA_BLOCKHOOK */
391
392 static int dd_handle_const(pTHX_ char *name) {
393   switch (PL_lex_inwhat) {
394     case OP_QR:
395     case OP_MATCH:
396     case OP_SUBST:
397     case OP_TRANS:
398     case OP_BACKTICK:
399     case OP_STRINGIFY:
400       return 0;
401       break;
402     default:
403       break;
404   }
405
406   if (strnEQ(PL_bufptr, "->", 2)) {
407     return 0;
408   }
409
410   {
411     char buf[256];
412     STRLEN len;
413     char *s = PL_bufptr;
414     STRLEN old_offset = PL_bufptr - SvPVX(PL_linestr);
415
416     s = scan_word(s, buf, sizeof buf, FALSE, &len);
417     if (strnEQ(buf, name, len)) {
418       char *d;
419       SV *inject = newSVpvn(SvPVX(PL_linestr), PL_bufptr - SvPVX(PL_linestr));
420       sv_catpvn(inject, buf, len);
421
422       d = peekspace(s);
423       sv_catpvn(inject, s, d - s);
424
425       if ((PL_bufend - d) >= 2 && strnEQ(d, "=>", 2)) {
426         return 0;
427       }
428
429       sv_catpv(inject, d);
430       dd_set_linestr(aTHX_ SvPV_nolen(inject));
431       PL_bufptr = SvPVX(PL_linestr) + old_offset;
432       SvREFCNT_dec (inject);
433     }
434   }
435
436   dd_linestr_callback(aTHX_ "const", name);
437
438   return 1;
439 }
440
441 #if !DD_CONST_VIA_RV2CV
442
443 STATIC OP *dd_ck_const(pTHX_ OP *o, void *user_data) {
444   int dd_flags;
445   char* name;
446
447   PERL_UNUSED_VAR(user_data);
448
449   if (DD_HAVE_PARSER && PL_expect == XOPERATOR) {
450     return o;
451   }
452
453   /* if this is set, we just grabbed a delimited string or something,
454      not a bareword, so NO TOUCHY */
455
456   if (DD_HAVE_LEX_STUFF)
457     return o;
458
459   /* don't try and look this up if it's not a string const */
460   if (!SvPOK(cSVOPo->op_sv))
461     return o;
462
463   name = SvPVX(cSVOPo->op_sv);
464
465   dd_flags = dd_is_declarator(aTHX_ name);
466
467   if (dd_flags == -1)
468     return o;
469
470   dd_handle_const(aTHX_ name);
471
472   return o;
473 }
474
475 #endif /* !DD_CONST_VIA_RV2CV */
476
477 static int initialized = 0;
478
479 MODULE = Devel::Declare  PACKAGE = Devel::Declare
480
481 PROTOTYPES: DISABLE
482
483 void
484 setup()
485   CODE:
486   if (!initialized++) {
487 #if DD_GROW_VIA_BLOCKHOOK
488     static BHK bhk;
489 #if PERL_VERSION_GE(5,13,6)
490     BhkENTRY_set(&bhk, bhk_start, dd_block_start);
491 #else /* <5.13.6 */
492     BhkENTRY_set(&bhk, start, dd_block_start);
493 #endif /* <5.13.6 */
494     Perl_blockhook_register(aTHX_ &bhk);
495 #else /* !DD_GROW_VIA_BLOCKHOOK */
496     hook_op_check(OP_ENTEREVAL, dd_ck_entereval, NULL);
497 #endif /* !DD_GROW_VIA_BLOCKHOOK */
498     hook_op_check(OP_RV2CV, dd_ck_rv2cv, NULL);
499 #if !DD_CONST_VIA_RV2CV
500     hook_op_check(OP_CONST, dd_ck_const, NULL);
501 #endif /* !DD_CONST_VIA_RV2CV */
502   }
503 #if !DD_GROW_VIA_BLOCKHOOK
504   filter_add(dd_filter_realloc, NULL);
505 #endif /* !DD_GROW_VIA_BLOCKHOOK */
506
507 char*
508 get_linestr()
509   CODE:
510     RETVAL = dd_get_linestr(aTHX);
511   OUTPUT:
512     RETVAL
513
514 void
515 set_linestr(char* new_value)
516   CODE:
517     dd_set_linestr(aTHX_ new_value);
518
519 char*
520 get_lex_stuff()
521   CODE:
522     RETVAL = dd_get_lex_stuff(aTHX);
523   OUTPUT:
524     RETVAL
525
526 void
527 clear_lex_stuff()
528   CODE:
529     dd_clear_lex_stuff(aTHX);
530
531 char*
532 get_curstash_name()
533   CODE:
534     RETVAL = dd_get_curstash_name(aTHX);
535   OUTPUT:
536     RETVAL
537
538 int
539 get_linestr_offset()
540   CODE:
541     RETVAL = dd_get_linestr_offset(aTHX);
542   OUTPUT:
543     RETVAL
544
545 int
546 toke_scan_word(int offset, int handle_package)
547   CODE:
548     RETVAL = dd_toke_scan_word(aTHX_ offset, handle_package);
549   OUTPUT:
550     RETVAL
551
552 int
553 toke_move_past_token(int offset);
554   CODE:
555     RETVAL = dd_toke_move_past_token(aTHX_ offset);
556   OUTPUT:
557     RETVAL
558
559 int
560 toke_scan_str(int offset);
561   CODE:
562     RETVAL = dd_toke_scan_str(aTHX_ offset);
563   OUTPUT:
564     RETVAL
565
566 int
567 toke_scan_ident(int offset)
568   CODE:
569     RETVAL = dd_toke_scan_ident(aTHX_ offset);
570   OUTPUT:
571     RETVAL
572
573 int
574 toke_skipspace(int offset)
575   CODE:
576     RETVAL = dd_toke_skipspace(aTHX_ offset);
577   OUTPUT:
578     RETVAL
579
580 int
581 get_in_declare()
582   CODE:
583     RETVAL = in_declare;
584   OUTPUT:
585     RETVAL
586
587 void
588 set_in_declare(int value)
589   CODE:
590     in_declare = value;
591
592 BOOT:
593 {
594   char *endptr;
595   char *debug_str = getenv ("DD_DEBUG");
596   if (debug_str) {
597     dd_debug = strtol (debug_str, &endptr, 10);
598     if (*endptr != '\0') {
599       dd_debug = 0;
600     }
601   }
602 }