Check if reallocation of PL_linestr is necessary before doing it.
[p5sagit/Devel-Declare.git] / Declare.xs
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4 #undef printf
5 #include "stolen_chunk_of_toke.c"
6 #include <stdio.h>
7 #include <string.h>
8
9 #ifndef Newx
10 # define Newx(v,n,t) New(0,v,n,t)
11 #endif /* !Newx */
12
13 #if 0
14 #define DD_DEBUG
15 #endif
16
17 #ifdef DD_DEBUG
18 #define DD_DEBUG_S printf("Buffer: %s\n", s);
19 #else
20 #define DD_DEBUG_S
21 #endif
22
23 #define LEX_NORMAL    10
24 #define LEX_INTERPNORMAL   9
25
26 /* flag to trigger removal of temporary declaree sub */
27
28 static int in_declare = 0;
29
30 /* in 5.10, PL_parser will be NULL if we aren't parsing, and PL_lex_stuff
31    is a lookup into it - so if anything else we can use to tell, so we
32    need to be a bit more careful if PL_parser exists */
33
34 #define DD_AM_LEXING_CHECK (PL_lex_state == LEX_NORMAL || PL_lex_state == LEX_INTERPNORMAL)
35
36 #ifdef PL_parser
37 #define DD_HAVE_PARSER PL_parser
38 #define DD_HAVE_LEX_STUFF (PL_parser && PL_lex_stuff)
39 #define DD_AM_LEXING (PL_parser && DD_AM_LEXING_CHECK)
40 #else
41 #define DD_HAVE_PARSER 1
42 #define DD_HAVE_LEX_STUFF PL_lex_stuff
43 #define DD_AM_LEXING DD_AM_LEXING_CHECK
44 #endif
45
46 /* thing that decides whether we're dealing with a declarator */
47
48 int dd_is_declarator(pTHX_ char* name) {
49   HV* is_declarator;
50   SV** is_declarator_pack_ref;
51   HV* is_declarator_pack_hash;
52   SV** is_declarator_flag_ref;
53   int dd_flags;
54
55   is_declarator = get_hv("Devel::Declare::declarators", FALSE);
56
57   if (!is_declarator)
58     return -1;
59
60   /* $declarators{$current_package_name} */
61
62   is_declarator_pack_ref = hv_fetch(is_declarator, HvNAME(PL_curstash),
63                              strlen(HvNAME(PL_curstash)), FALSE);
64
65   if (!is_declarator_pack_ref || !SvROK(*is_declarator_pack_ref))
66     return -1; /* not a hashref */
67
68   is_declarator_pack_hash = (HV*) SvRV(*is_declarator_pack_ref);
69
70   /* $declarators{$current_package_name}{$name} */
71
72   is_declarator_flag_ref = hv_fetch(
73     is_declarator_pack_hash, name,
74     strlen(name), FALSE
75   );
76
77   /* requires SvIOK as well as TRUE since flags not being an int is useless */
78
79   if (!is_declarator_flag_ref
80         || !SvIOK(*is_declarator_flag_ref) 
81         || !SvTRUE(*is_declarator_flag_ref))
82     return -1;
83
84   dd_flags = SvIVX(*is_declarator_flag_ref);
85
86   return dd_flags;
87 }
88
89 /* callback thingy */
90
91 void dd_linestr_callback (pTHX_ char* type, char* name) {
92
93   char* linestr = SvPVX(PL_linestr);
94   int offset = PL_bufptr - linestr;
95
96   dSP;
97
98   ENTER;
99   SAVETMPS;
100
101   PUSHMARK(SP);
102   XPUSHs(sv_2mortal(newSVpv(type, 0)));
103   XPUSHs(sv_2mortal(newSVpv(name, 0)));
104   XPUSHs(sv_2mortal(newSViv(offset)));
105   PUTBACK;
106
107   call_pv("Devel::Declare::linestr_callback", G_VOID|G_DISCARD);
108
109   FREETMPS;
110   LEAVE;
111 }
112
113 char* dd_get_linestr(pTHX) {
114   return SvPVX(PL_linestr);
115 }
116
117 void dd_set_linestr(pTHX_ char* new_value) {
118   int new_len = strlen(new_value);
119   char* old_linestr = SvPVX(PL_linestr);
120
121   if (SvLEN(PL_linestr) < new_len) {
122     croak("forced to realloc PL_linestr for line %s, bailing out before we crash harder", SvPVX(PL_linestr));
123   }
124
125   SvGROW(PL_linestr, new_len);
126
127   memcpy(SvPVX(PL_linestr), new_value, new_len+1);
128
129   SvCUR_set(PL_linestr, new_len);
130
131   PL_bufend = SvPVX(PL_linestr) + new_len;
132 }
133
134 char* dd_get_lex_stuff(pTHX) {
135   return (DD_HAVE_LEX_STUFF ? SvPVX(PL_lex_stuff) : "");
136 }
137
138 char* dd_clear_lex_stuff(pTHX) {
139   if (DD_HAVE_PARSER)
140     PL_lex_stuff = (SV*)NULL;
141 }
142
143 char* dd_get_curstash_name(pTHX) {
144   return HvNAME(PL_curstash);
145 }
146
147 int dd_get_linestr_offset(pTHX) {
148   char* linestr = SvPVX(PL_linestr);
149   return PL_bufptr - linestr;
150 }
151
152 char* dd_move_past_token (pTHX_ char* s) {
153
154   /*
155    *   buffer will be at the beginning of the declarator, -unless- the
156    *   declarator is at EOL in which case it'll be the next useful line
157    *   so we don't short-circuit out if we don't find the declarator
158    */
159
160   while (s < PL_bufend && isSPACE(*s)) s++;
161   if (memEQ(s, PL_tokenbuf, strlen(PL_tokenbuf)))
162     s += strlen(PL_tokenbuf);
163   return s;
164 }
165
166 int dd_toke_move_past_token (pTHX_ int offset) {
167   char* base_s = SvPVX(PL_linestr) + offset;
168   char* s = dd_move_past_token(aTHX_ base_s);
169   return s - base_s;
170 }
171
172 int dd_toke_scan_word(pTHX_ int offset, int handle_package) {
173   char tmpbuf[sizeof PL_tokenbuf];
174   char* base_s = SvPVX(PL_linestr) + offset;
175   STRLEN len;
176   char* s = scan_word(base_s, tmpbuf, sizeof tmpbuf, handle_package, &len);
177   return s - base_s;
178 }
179
180 int dd_toke_scan_str(pTHX_ int offset) {
181   char* base_s = SvPVX(PL_linestr) + offset;
182   char* s = scan_str(base_s, FALSE, FALSE);
183   return s - base_s;
184 }
185
186 int dd_toke_skipspace(pTHX_ int offset) {
187   char* base_s = SvPVX(PL_linestr) + offset;
188   char* s = skipspace(base_s);
189   return s - base_s;
190 }
191
192 /* replacement PL_check rv2cv entry */
193
194 STATIC OP *(*dd_old_ck_rv2cv)(pTHX_ OP *op);
195
196 STATIC OP *dd_ck_rv2cv(pTHX_ OP *o) {
197   OP* kid;
198   int dd_flags;
199   char* cb_args[6];
200
201   o = dd_old_ck_rv2cv(aTHX_ o); /* let the original do its job */
202
203   if (in_declare) {
204     cb_args[0] = NULL;
205 #ifdef DD_DEBUG
206     printf("Deconstructing declare\n");
207     printf("PL_bufptr: %s\n", PL_bufptr);
208     printf("bufend at: %i\n", PL_bufend - PL_bufptr);
209     printf("linestr: %s\n", SvPVX(PL_linestr));
210     printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr));
211 #endif
212     call_argv("Devel::Declare::done_declare", G_VOID|G_DISCARD, cb_args);
213 #ifdef DD_DEBUG
214     printf("PL_bufptr: %s\n", PL_bufptr);
215     printf("bufend at: %i\n", PL_bufend - PL_bufptr);
216     printf("linestr: %s\n", SvPVX(PL_linestr));
217     printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr));
218     printf("actual len: %i\n", strlen(PL_bufptr));
219 #endif
220     return o;
221   }
222
223   kid = cUNOPo->op_first;
224
225   if (kid->op_type != OP_GV) /* not a GV so ignore */
226     return o;
227
228   if (!DD_AM_LEXING)
229     return o; /* not lexing? */
230
231 #ifdef DD_DEBUG
232   printf("Checking GV %s -> %s\n", HvNAME(GvSTASH(kGVOP_gv)), GvNAME(kGVOP_gv));
233 #endif
234
235   dd_flags = dd_is_declarator(aTHX_ GvNAME(kGVOP_gv));
236
237   if (dd_flags == -1)
238     return o;
239
240 #ifdef DD_DEBUG
241   printf("dd_flags are: %i\n", dd_flags);
242 #endif
243
244 #ifdef DD_DEBUG
245   printf("PL_tokenbuf: %s\n", PL_tokenbuf);
246 #endif
247
248   dd_linestr_callback(aTHX_ "rv2cv", GvNAME(kGVOP_gv));
249
250   return o;
251 }
252
253 STATIC OP *(*dd_old_ck_entereval)(pTHX_ OP *op);
254
255 OP* dd_pp_entereval(pTHX) {
256   dSP;
257   dPOPss;
258   STRLEN len;
259   const char* s;
260   if (SvPOK(sv)) {
261 #ifdef DD_DEBUG
262     printf("mangling eval sv\n");
263 #endif
264     if (SvREADONLY(sv))
265       sv = sv_2mortal(newSVsv(sv));
266     s = SvPVX(sv);
267     len = SvCUR(sv);
268     if (!len || s[len-1] != ';') {
269       if (!(SvFLAGS(sv) & SVs_TEMP))
270         sv = sv_2mortal(newSVsv(sv));
271       sv_catpvn(sv, "\n;", 2);
272     }
273     SvGROW(sv, 8192);
274   }
275   PUSHs(sv);
276   return PL_ppaddr[OP_ENTEREVAL](aTHX);
277 }
278
279 STATIC OP *dd_ck_entereval(pTHX_ OP *o) {
280   o = dd_old_ck_entereval(aTHX_ o); /* let the original do its job */
281   if (o->op_ppaddr == PL_ppaddr[OP_ENTEREVAL])
282     o->op_ppaddr = dd_pp_entereval;
283   return o;
284 }
285
286 static I32 dd_filter_realloc(pTHX_ int idx, SV *sv, int maxlen)
287 {
288   const I32 count = FILTER_READ(idx+1, sv, maxlen);
289   SvGROW(sv, 8192); /* please try not to have a line longer than this :) */
290   /* filter_del(dd_filter_realloc); */
291   return count;
292 }
293
294 STATIC OP *(*dd_old_ck_const)(pTHX_ OP*op);
295
296 STATIC OP *dd_ck_const(pTHX_ OP *o) {
297   int dd_flags;
298   char* s;
299   char* name;
300
301   o = dd_old_ck_const(aTHX_ o); /* let the original do its job */
302
303   /* if this is set, we just grabbed a delimited string or something,
304      not a bareword, so NO TOUCHY */
305
306   if (DD_HAVE_LEX_STUFF)
307     return o;
308
309   /* don't try and look this up if it's not a string const */
310   if (!SvPOK(cSVOPo->op_sv))
311     return o;
312
313   name = SvPVX(cSVOPo->op_sv);
314
315   dd_flags = dd_is_declarator(aTHX_ name);
316
317   if (dd_flags == -1)
318     return o;
319
320   dd_linestr_callback(aTHX_ "const", name);
321
322   return o;  
323 }
324
325 static int initialized = 0;
326
327 MODULE = Devel::Declare  PACKAGE = Devel::Declare
328
329 PROTOTYPES: DISABLE
330
331 void
332 setup()
333   CODE:
334   if (!initialized++) {
335     dd_old_ck_rv2cv = PL_check[OP_RV2CV];
336     PL_check[OP_RV2CV] = dd_ck_rv2cv;
337     dd_old_ck_entereval = PL_check[OP_ENTEREVAL];
338     PL_check[OP_ENTEREVAL] = dd_ck_entereval;
339     dd_old_ck_const = PL_check[OP_CONST];
340     PL_check[OP_CONST] = dd_ck_const;
341   }
342   filter_add(dd_filter_realloc, NULL);
343
344 char*
345 get_linestr()
346   CODE:
347     RETVAL = dd_get_linestr(aTHX);
348   OUTPUT:
349     RETVAL
350
351 void
352 set_linestr(char* new_value)
353   CODE:
354     dd_set_linestr(aTHX_ new_value);
355
356 char*
357 get_lex_stuff()
358   CODE:
359     RETVAL = dd_get_lex_stuff(aTHX);
360   OUTPUT:
361     RETVAL
362
363 void
364 clear_lex_stuff()
365   CODE:
366     dd_clear_lex_stuff(aTHX);
367
368 char*
369 get_curstash_name()
370   CODE:
371     RETVAL = dd_get_curstash_name(aTHX);
372   OUTPUT:
373     RETVAL
374
375 int
376 get_linestr_offset()
377   CODE:
378     RETVAL = dd_get_linestr_offset(aTHX);
379   OUTPUT:
380     RETVAL
381
382 int
383 toke_scan_word(int offset, int handle_package)
384   CODE:
385     RETVAL = dd_toke_scan_word(aTHX_ offset, handle_package);
386   OUTPUT:
387     RETVAL
388
389 int
390 toke_move_past_token(int offset);
391   CODE:
392     RETVAL = dd_toke_move_past_token(aTHX_ offset);
393   OUTPUT:
394     RETVAL
395
396 int
397 toke_scan_str(int offset);
398   CODE:
399     RETVAL = dd_toke_scan_str(aTHX_ offset);
400   OUTPUT:
401     RETVAL
402
403 int
404 toke_skipspace(int offset)
405   CODE:
406     RETVAL = dd_toke_skipspace(aTHX_ offset);
407   OUTPUT:
408     RETVAL
409
410 int
411 get_in_declare()
412   CODE:
413     RETVAL = in_declare;
414   OUTPUT:
415     RETVAL
416
417 void
418 set_in_declare(int value)
419   CODE:
420     in_declare = value;