Don't define PERL_CORE.
[p5sagit/Devel-Declare.git] / Declare.xs
1 #define PERL_NO_GET_CONTEXT
2 #include "EXTERN.h"
3 #include "perl.h"
4 #include "XSUB.h"
5 #undef printf
6 #include "stolen_chunk_of_toke.c"
7 #include <stdio.h>
8 #include <string.h>
9
10 #ifndef Newx
11 # define Newx(v,n,t) New(0,v,n,t)
12 #endif /* !Newx */
13
14 #if 0
15 #define DD_DEBUG
16 #endif
17
18 #ifdef DD_DEBUG
19 #define DD_DEBUG_S printf("Buffer: %s\n", s);
20 #else
21 #define DD_DEBUG_S
22 #endif
23
24 #define LEX_NORMAL    10
25 #define LEX_INTERPNORMAL   9
26
27 /* flag to trigger removal of temporary declaree sub */
28
29 static int in_declare = 0;
30
31 /* in 5.10, PL_parser will be NULL if we aren't parsing, and PL_lex_stuff
32    is a lookup into it - so if anything else we can use to tell, so we
33    need to be a bit more careful if PL_parser exists */
34
35 #define DD_AM_LEXING_CHECK (PL_lex_state == LEX_NORMAL || PL_lex_state == LEX_INTERPNORMAL)
36
37 #ifdef PL_parser
38 #define DD_HAVE_PARSER PL_parser
39 #define DD_HAVE_LEX_STUFF (PL_parser && PL_lex_stuff)
40 #define DD_AM_LEXING (PL_parser && DD_AM_LEXING_CHECK)
41 #else
42 #define DD_HAVE_PARSER 1
43 #define DD_HAVE_LEX_STUFF PL_lex_stuff
44 #define DD_AM_LEXING DD_AM_LEXING_CHECK
45 #endif
46
47 /* thing that decides whether we're dealing with a declarator */
48
49 int dd_is_declarator(pTHX_ char* name) {
50   HV* is_declarator;
51   SV** is_declarator_pack_ref;
52   HV* is_declarator_pack_hash;
53   SV** is_declarator_flag_ref;
54   int dd_flags;
55
56   is_declarator = get_hv("Devel::Declare::declarators", FALSE);
57
58   if (!is_declarator)
59     return -1;
60
61   /* $declarators{$current_package_name} */
62
63   is_declarator_pack_ref = hv_fetch(is_declarator, HvNAME(PL_curstash),
64                              strlen(HvNAME(PL_curstash)), FALSE);
65
66   if (!is_declarator_pack_ref || !SvROK(*is_declarator_pack_ref))
67     return -1; /* not a hashref */
68
69   is_declarator_pack_hash = (HV*) SvRV(*is_declarator_pack_ref);
70
71   /* $declarators{$current_package_name}{$name} */
72
73   is_declarator_flag_ref = hv_fetch(
74     is_declarator_pack_hash, name,
75     strlen(name), FALSE
76   );
77
78   /* requires SvIOK as well as TRUE since flags not being an int is useless */
79
80   if (!is_declarator_flag_ref
81         || !SvIOK(*is_declarator_flag_ref) 
82         || !SvTRUE(*is_declarator_flag_ref))
83     return -1;
84
85   dd_flags = SvIVX(*is_declarator_flag_ref);
86
87   return dd_flags;
88 }
89
90 /* callback thingy */
91
92 void dd_linestr_callback (pTHX_ char* type, char* name) {
93
94   char* linestr = SvPVX(PL_linestr);
95   int offset = PL_bufptr - linestr;
96
97   dSP;
98
99   ENTER;
100   SAVETMPS;
101
102   PUSHMARK(SP);
103   XPUSHs(sv_2mortal(newSVpv(type, 0)));
104   XPUSHs(sv_2mortal(newSVpv(name, 0)));
105   XPUSHs(sv_2mortal(newSViv(offset)));
106   PUTBACK;
107
108   call_pv("Devel::Declare::linestr_callback", G_VOID|G_DISCARD);
109
110   FREETMPS;
111   LEAVE;
112 }
113
114 char* dd_get_linestr(pTHX) {
115   return SvPVX(PL_linestr);
116 }
117
118 void dd_set_linestr(pTHX_ char* new_value) {
119   int new_len = strlen(new_value);
120   char* old_linestr = SvPVX(PL_linestr);
121
122   SvGROW(PL_linestr, new_len);
123
124   if (SvPVX(PL_linestr) != old_linestr)
125     croak("forced to realloc PL_linestr for line %s, bailing out before we crash harder", SvPVX(PL_linestr));
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;