Some kind of fix or workaround for phaylon's parameterized role bug in MXD.
[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
7dd7d008 14#define DD_DEBUGf_UPDATED_LINESTR 1
15#define DD_DEBUGf_TRACE 2
16
17#define DD_DEBUG_UPDATED_LINESTR (dd_debug & DD_DEBUGf_UPDATED_LINESTR)
18#define DD_DEBUG_TRACE (dd_debug & DD_DEBUGf_TRACE)
e81bee92 19static int dd_debug = 0;
c630715a 20
94caac6e 21#define LEX_NORMAL 10
22#define LEX_INTERPNORMAL 9
23
94caac6e 24/* flag to trigger removal of temporary declaree sub */
25
26static int in_declare = 0;
27
96f12726 28/* in 5.10, PL_parser will be NULL if we aren't parsing, and PL_lex_stuff
29 is a lookup into it - so if anything else we can use to tell, so we
30 need to be a bit more careful if PL_parser exists */
31
32#define DD_AM_LEXING_CHECK (PL_lex_state == LEX_NORMAL || PL_lex_state == LEX_INTERPNORMAL)
33
f19b3507 34#if defined(PL_parser) || defined(PERL_5_9_PLUS)
96f12726 35#define DD_HAVE_PARSER PL_parser
36#define DD_HAVE_LEX_STUFF (PL_parser && PL_lex_stuff)
37#define DD_AM_LEXING (PL_parser && DD_AM_LEXING_CHECK)
38#else
39#define DD_HAVE_PARSER 1
40#define DD_HAVE_LEX_STUFF PL_lex_stuff
41#define DD_AM_LEXING DD_AM_LEXING_CHECK
42#endif
43
72f20f69 44/* thing that decides whether we're dealing with a declarator */
45
46int dd_is_declarator(pTHX_ char* name) {
47 HV* is_declarator;
48 SV** is_declarator_pack_ref;
49 HV* is_declarator_pack_hash;
50 SV** is_declarator_flag_ref;
51 int dd_flags;
52
53 is_declarator = get_hv("Devel::Declare::declarators", FALSE);
54
55 if (!is_declarator)
56 return -1;
57
569ac469 58 /* $declarators{$current_package_name} */
59
9603b8dc 60 if (!HvNAME(PL_curstash))
f2a41aa1 61 return -1;
9603b8dc 62
72f20f69 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
569ac469 71 /* $declarators{$current_package_name}{$name} */
72
72f20f69 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
48ee5c99 81 || !SvIOK(*is_declarator_flag_ref)
72f20f69 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
569ac469 90/* callback thingy */
91
a9bd9b5e 92void dd_linestr_callback (pTHX_ char* type, char* name) {
569ac469 93
94 char* linestr = SvPVX(PL_linestr);
a9bd9b5e 95 int offset = PL_bufptr - linestr;
569ac469 96
569ac469 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
04a8a223 108 call_pv("Devel::Declare::linestr_callback", G_VOID|G_DISCARD);
569ac469 109
569ac469 110 FREETMPS;
111 LEAVE;
112}
113
114char* dd_get_linestr(pTHX) {
5f0b59d5 115 if (!DD_HAVE_PARSER) {
116 return NULL;
117 }
569ac469 118 return SvPVX(PL_linestr);
119}
120
121void dd_set_linestr(pTHX_ char* new_value) {
6f5220b7 122 unsigned int new_len = strlen(new_value);
569ac469 123
ce9a252b 124 if (SvLEN(PL_linestr) < new_len) {
41db92e3 125 croak("PL_linestr not long enough, was Devel::Declare loaded soon enough in %s",
126 CopFILE(&PL_compiling)
127 );
ce9a252b 128 }
129
569ac469 130
131 memcpy(SvPVX(PL_linestr), new_value, new_len+1);
132
133 SvCUR_set(PL_linestr, new_len);
134
135 PL_bufend = SvPVX(PL_linestr) + new_len;
7dd7d008 136
87195072 137 if ( DD_DEBUG_UPDATED_LINESTR && PERLDB_LINE && PL_curstash != PL_debstash) {
7dd7d008 138 // Cribbed from toke.c
139 SV * const sv = NEWSV(85,0);
140
141 sv_upgrade(sv, SVt_PVMG);
142 sv_setpvn(sv,PL_bufptr,PL_bufend-PL_bufptr);
143 (void)SvIOK_on(sv);
144 SvIV_set(sv, 0);
145 av_store(CopFILEAV(&PL_compiling),(I32)CopLINE(&PL_compiling),sv);
146 }
569ac469 147}
148
04a8a223 149char* dd_get_lex_stuff(pTHX) {
96f12726 150 return (DD_HAVE_LEX_STUFF ? SvPVX(PL_lex_stuff) : "");
04a8a223 151}
152
bd85a06e 153void dd_clear_lex_stuff(pTHX) {
96f12726 154 if (DD_HAVE_PARSER)
f05cbc90 155 PL_lex_stuff = (SV*)NULL;
04a8a223 156}
157
158char* dd_get_curstash_name(pTHX) {
159 return HvNAME(PL_curstash);
160}
161
022eb0cc 162int dd_get_linestr_offset(pTHX) {
0da63271 163 char* linestr;
164 if (!DD_HAVE_PARSER) {
165 return -1;
166 }
167 linestr = SvPVX(PL_linestr);
022eb0cc 168 return PL_bufptr - linestr;
169}
170
840ebcbb 171char* dd_move_past_token (pTHX_ char* s) {
923c07a8 172
173 /*
174 * buffer will be at the beginning of the declarator, -unless- the
175 * declarator is at EOL in which case it'll be the next useful line
176 * so we don't short-circuit out if we don't find the declarator
177 */
178
179 while (s < PL_bufend && isSPACE(*s)) s++;
180 if (memEQ(s, PL_tokenbuf, strlen(PL_tokenbuf)))
181 s += strlen(PL_tokenbuf);
182 return s;
183}
184
840ebcbb 185int dd_toke_move_past_token (pTHX_ int offset) {
04a8a223 186 char* base_s = SvPVX(PL_linestr) + offset;
187 char* s = dd_move_past_token(aTHX_ base_s);
188 return s - base_s;
189}
190
923c07a8 191int dd_toke_scan_word(pTHX_ int offset, int handle_package) {
192 char tmpbuf[sizeof PL_tokenbuf];
193 char* base_s = SvPVX(PL_linestr) + offset;
194 STRLEN len;
195 char* s = scan_word(base_s, tmpbuf, sizeof tmpbuf, handle_package, &len);
196 return s - base_s;
197}
198
f11d21b2 199int dd_toke_scan_ident(pTHX_ int offset) {
200 char tmpbuf[sizeof PL_tokenbuf];
201 char* base_s = SvPVX(PL_linestr) + offset;
f11d21b2 202 char* s = scan_ident(base_s, PL_bufend, tmpbuf, sizeof tmpbuf, 0);
203 return s - base_s;
204}
205
923c07a8 206int dd_toke_scan_str(pTHX_ int offset) {
19b277fc 207 STRLEN remaining = sv_len(PL_linestr) - offset;
86964fb3 208 SV* line_copy = newSVsv(PL_linestr);
923c07a8 209 char* base_s = SvPVX(PL_linestr) + offset;
210 char* s = scan_str(base_s, FALSE, FALSE);
86964fb3 211 if (s != base_s && sv_len(PL_lex_stuff) > remaining) {
212 int ret = (s - SvPVX(PL_linestr)) + remaining;
213 sv_catsv(line_copy, PL_linestr);
214 dd_set_linestr(aTHX_ SvPV_nolen(line_copy));
215 SvREFCNT_dec(line_copy);
216 return ret;
217 }
923c07a8 218 return s - base_s;
219}
220
221int dd_toke_skipspace(pTHX_ int offset) {
222 char* base_s = SvPVX(PL_linestr) + offset;
a25db2dc 223 char* s = skipspace_force(base_s);
923c07a8 224 return s - base_s;
225}
226
94caac6e 227/* replacement PL_check rv2cv entry */
228
6e67754a 229STATIC OP *dd_ck_rv2cv(pTHX_ OP *o, void *user_data) {
19b7ec0f 230 dSP;
94caac6e 231 OP* kid;
0ba8c7aa 232 int dd_flags;
94caac6e 233
6f5220b7 234 PERL_UNUSED_VAR(user_data);
235
94caac6e 236 if (in_declare) {
7dd7d008 237 if (DD_DEBUG_TRACE) {
e81bee92 238 printf("Deconstructing declare\n");
239 printf("PL_bufptr: %s\n", PL_bufptr);
240 printf("bufend at: %i\n", PL_bufend - PL_bufptr);
241 printf("linestr: %s\n", SvPVX(PL_linestr));
242 printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr));
243 }
001d53d0 244
001d53d0 245 ENTER;
246 SAVETMPS;
19b7ec0f 247
001d53d0 248 PUSHMARK(SP);
19b7ec0f 249
001d53d0 250 call_pv("Devel::Declare::done_declare", G_VOID|G_DISCARD);
251
252 FREETMPS;
253 LEAVE;
254
7dd7d008 255 if (DD_DEBUG_TRACE) {
e81bee92 256 printf("PL_bufptr: %s\n", PL_bufptr);
257 printf("bufend at: %i\n", PL_bufend - PL_bufptr);
258 printf("linestr: %s\n", SvPVX(PL_linestr));
259 printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr));
260 printf("actual len: %i\n", strlen(PL_bufptr));
261 }
94caac6e 262 return o;
263 }
264
265 kid = cUNOPo->op_first;
266
267 if (kid->op_type != OP_GV) /* not a GV so ignore */
268 return o;
269
96f12726 270 if (!DD_AM_LEXING)
94caac6e 271 return o; /* not lexing? */
272
7dd7d008 273 if (DD_DEBUG_TRACE) {
e81bee92 274 printf("Checking GV %s -> %s\n", HvNAME(GvSTASH(kGVOP_gv)), GvNAME(kGVOP_gv));
275 }
840ebcbb 276
72f20f69 277 dd_flags = dd_is_declarator(aTHX_ GvNAME(kGVOP_gv));
94caac6e 278
72f20f69 279 if (dd_flags == -1)
94caac6e 280 return o;
281
7dd7d008 282 if (DD_DEBUG_TRACE) {
e81bee92 283 printf("dd_flags are: %i\n", dd_flags);
284 printf("PL_tokenbuf: %s\n", PL_tokenbuf);
285 }
840ebcbb 286
a9bd9b5e 287 dd_linestr_callback(aTHX_ "rv2cv", GvNAME(kGVOP_gv));
94caac6e 288
53e3ab32 289 return o;
290}
291
b7505981 292OP* dd_pp_entereval(pTHX) {
293 dSP;
b7505981 294 STRLEN len;
295 const char* s;
c93bc23e 296 SV *sv;
297#ifdef PERL_5_9_PLUS
298 SV *saved_hh;
299 if (PL_op->op_private & OPpEVAL_HAS_HH) {
300 saved_hh = POPs;
301 }
302#endif
303 sv = POPs;
b7505981 304 if (SvPOK(sv)) {
7dd7d008 305 if (DD_DEBUG_TRACE) {
e81bee92 306 printf("mangling eval sv\n");
307 }
b7505981 308 if (SvREADONLY(sv))
309 sv = sv_2mortal(newSVsv(sv));
310 s = SvPVX(sv);
311 len = SvCUR(sv);
312 if (!len || s[len-1] != ';') {
313 if (!(SvFLAGS(sv) & SVs_TEMP))
314 sv = sv_2mortal(newSVsv(sv));
315 sv_catpvn(sv, "\n;", 2);
53e3ab32 316 }
b7505981 317 SvGROW(sv, 8192);
53e3ab32 318 }
b7505981 319 PUSHs(sv);
c93bc23e 320#ifdef PERL_5_9_PLUS
321 if (PL_op->op_private & OPpEVAL_HAS_HH) {
322 PUSHs(saved_hh);
323 }
324#endif
b7505981 325 return PL_ppaddr[OP_ENTEREVAL](aTHX);
326}
53e3ab32 327
6e67754a 328STATIC OP *dd_ck_entereval(pTHX_ OP *o, void *user_data) {
6f5220b7 329 PERL_UNUSED_VAR(user_data);
330
d8e65fc8 331 if (o->op_ppaddr == PL_ppaddr[OP_ENTEREVAL])
332 o->op_ppaddr = dd_pp_entereval;
94caac6e 333 return o;
334}
335
6a0bcf35 336static I32 dd_filter_realloc(pTHX_ int idx, SV *sv, int maxlen)
337{
338 const I32 count = FILTER_READ(idx+1, sv, maxlen);
339 SvGROW(sv, 8192); /* please try not to have a line longer than this :) */
340 /* filter_del(dd_filter_realloc); */
341 return count;
342}
343
6e67754a 344STATIC OP *dd_ck_const(pTHX_ OP *o, void *user_data) {
bedac9ff 345 int dd_flags;
04a8a223 346 char* name;
bedac9ff 347
6f5220b7 348 PERL_UNUSED_VAR(user_data);
349
e21e91fe 350 if (DD_HAVE_PARSER && PL_expect == XOPERATOR) {
351 return o;
352 }
353
34335b63 354 /* if this is set, we just grabbed a delimited string or something,
355 not a bareword, so NO TOUCHY */
356
79abf18a 357 if (DD_HAVE_LEX_STUFF)
34335b63 358 return o;
359
bedac9ff 360 /* don't try and look this up if it's not a string const */
361 if (!SvPOK(cSVOPo->op_sv))
362 return o;
363
04a8a223 364 name = SvPVX(cSVOPo->op_sv);
365
366 dd_flags = dd_is_declarator(aTHX_ name);
bedac9ff 367
a9fb5fb1 368 if (dd_flags == -1)
bedac9ff 369 return o;
370
c48ac26f 371 switch (PL_lex_inwhat) {
372 case OP_QR:
373 case OP_MATCH:
374 case OP_SUBST:
375 case OP_TRANS:
f2a41aa1 376 case OP_BACKTICK:
377 case OP_STRINGIFY:
c48ac26f 378 return o;
379 break;
380 default:
381 break;
8d96afb7 382 }
48ee5c99 383
a25db2dc 384 if (strnEQ(PL_bufptr, "->", 2)) {
954da332 385 return o;
386 }
387
a25db2dc 388 {
389 char buf[256];
390 STRLEN len;
391 char *s = PL_bufptr;
392 STRLEN old_offset = PL_bufptr - SvPVX(PL_linestr);
393
394 s = scan_word(s, buf, sizeof buf, FALSE, &len);
395 if (strnEQ(buf, name, len)) {
396 char *d;
c0439e97 397 SV *inject = newSVpvn(SvPVX(PL_linestr), PL_bufptr - SvPVX(PL_linestr));
a25db2dc 398 sv_catpvn(inject, buf, len);
399
400 d = peekspace(s);
401 sv_catpvn(inject, s, d - s);
402
403 if ((PL_bufend - d) >= 2 && strnEQ(d, "=>", 2)) {
404 return o;
405 }
406
407 sv_catpv(inject, d);
408 dd_set_linestr(aTHX_ SvPV_nolen(inject));
409 PL_bufptr = SvPVX(PL_linestr) + old_offset;
410 SvREFCNT_dec (inject);
411 }
412 }
413
a9bd9b5e 414 dd_linestr_callback(aTHX_ "const", name);
bedac9ff 415
3ea50944 416 return o;
417}
418
94caac6e 419static int initialized = 0;
420
421MODULE = Devel::Declare PACKAGE = Devel::Declare
422
423PROTOTYPES: DISABLE
424
425void
426setup()
427 CODE:
428 if (!initialized++) {
6e67754a 429 hook_op_check(OP_RV2CV, dd_ck_rv2cv, NULL);
430 hook_op_check(OP_ENTEREVAL, dd_ck_entereval, NULL);
431 hook_op_check(OP_CONST, dd_ck_const, NULL);
94caac6e 432 }
6a0bcf35 433 filter_add(dd_filter_realloc, NULL);
569ac469 434
435char*
436get_linestr()
437 CODE:
438 RETVAL = dd_get_linestr(aTHX);
439 OUTPUT:
440 RETVAL
923c07a8 441
442void
443set_linestr(char* new_value)
444 CODE:
445 dd_set_linestr(aTHX_ new_value);
446
04a8a223 447char*
448get_lex_stuff()
449 CODE:
450 RETVAL = dd_get_lex_stuff(aTHX);
451 OUTPUT:
452 RETVAL
453
454void
455clear_lex_stuff()
456 CODE:
457 dd_clear_lex_stuff(aTHX);
458
459char*
460get_curstash_name()
461 CODE:
462 RETVAL = dd_get_curstash_name(aTHX);
463 OUTPUT:
464 RETVAL
465
923c07a8 466int
022eb0cc 467get_linestr_offset()
468 CODE:
469 RETVAL = dd_get_linestr_offset(aTHX);
470 OUTPUT:
471 RETVAL
472
473int
923c07a8 474toke_scan_word(int offset, int handle_package)
475 CODE:
476 RETVAL = dd_toke_scan_word(aTHX_ offset, handle_package);
477 OUTPUT:
478 RETVAL
479
480int
04a8a223 481toke_move_past_token(int offset);
482 CODE:
483 RETVAL = dd_toke_move_past_token(aTHX_ offset);
484 OUTPUT:
485 RETVAL
486
487int
923c07a8 488toke_scan_str(int offset);
489 CODE:
490 RETVAL = dd_toke_scan_str(aTHX_ offset);
491 OUTPUT:
492 RETVAL
493
494int
f11d21b2 495toke_scan_ident(int offset)
496 CODE:
497 RETVAL = dd_toke_scan_ident(aTHX_ offset);
498 OUTPUT:
499 RETVAL
500
501int
923c07a8 502toke_skipspace(int offset)
503 CODE:
504 RETVAL = dd_toke_skipspace(aTHX_ offset);
505 OUTPUT:
506 RETVAL
04a8a223 507
508int
509get_in_declare()
510 CODE:
511 RETVAL = in_declare;
512 OUTPUT:
513 RETVAL
514
515void
516set_in_declare(int value)
517 CODE:
518 in_declare = value;
e81bee92 519
520BOOT:
0a3b37d1 521{
87195072 522 char *endptr;
523 char *debug_str = getenv ("DD_DEBUG");
524 if (debug_str) {
525 dd_debug = strtol (debug_str, &endptr, 10);
526 if (*endptr != '\0') {
527 dd_debug = 0;
528 }
e81bee92 529 }
0a3b37d1 530}