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