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