partially substitute for unexported symbols
[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
023db2fb 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
9673d7ca 16#ifndef Newx
17# define Newx(v,n,t) New(0,v,n,t)
18#endif /* !Newx */
19
7dd7d008 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)
e81bee92 25static int dd_debug = 0;
c630715a 26
023db2fb 27#define DD_CONST_VIA_RV2CV PERL_VERSION_GE(5,11,2)
28
6ed8c948 29#define DD_GROW_VIA_BLOCKHOOK PERL_VERSION_GE(5,13,3)
30
94caac6e 31#define LEX_NORMAL 10
32#define LEX_INTERPNORMAL 9
33
e8df925b 34/* please try not to have a line longer than this :) */
35
36#define DD_PREFERRED_LINESTR_SIZE 16384
37
94caac6e 38/* flag to trigger removal of temporary declaree sub */
39
40static int in_declare = 0;
41
96f12726 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
f19b3507 48#if defined(PL_parser) || defined(PERL_5_9_PLUS)
96f12726 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
72f20f69 58/* thing that decides whether we're dealing with a declarator */
59
60int 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;
ec25cea7 66 char* curstash_name;
72f20f69 67
68 is_declarator = get_hv("Devel::Declare::declarators", FALSE);
69
70 if (!is_declarator)
71 return -1;
72
569ac469 73 /* $declarators{$current_package_name} */
74
ec25cea7 75 curstash_name = HvNAME(PL_curstash);
76 if (!curstash_name)
f2a41aa1 77 return -1;
9603b8dc 78
ec25cea7 79 is_declarator_pack_ref = hv_fetch(is_declarator, curstash_name,
80 strlen(curstash_name), FALSE);
72f20f69 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
569ac469 87 /* $declarators{$current_package_name}{$name} */
88
72f20f69 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
48ee5c99 97 || !SvIOK(*is_declarator_flag_ref)
72f20f69 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
569ac469 106/* callback thingy */
107
a9bd9b5e 108void dd_linestr_callback (pTHX_ char* type, char* name) {
569ac469 109
110 char* linestr = SvPVX(PL_linestr);
a9bd9b5e 111 int offset = PL_bufptr - linestr;
569ac469 112
569ac469 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
04a8a223 124 call_pv("Devel::Declare::linestr_callback", G_VOID|G_DISCARD);
569ac469 125
569ac469 126 FREETMPS;
127 LEAVE;
128}
129
130char* dd_get_linestr(pTHX) {
5f0b59d5 131 if (!DD_HAVE_PARSER) {
132 return NULL;
133 }
569ac469 134 return SvPVX(PL_linestr);
135}
136
137void dd_set_linestr(pTHX_ char* new_value) {
6f5220b7 138 unsigned int new_len = strlen(new_value);
569ac469 139
ce9a252b 140 if (SvLEN(PL_linestr) < new_len) {
41db92e3 141 croak("PL_linestr not long enough, was Devel::Declare loaded soon enough in %s",
142 CopFILE(&PL_compiling)
143 );
ce9a252b 144 }
145
569ac469 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;
7dd7d008 152
87195072 153 if ( DD_DEBUG_UPDATED_LINESTR && PERLDB_LINE && PL_curstash != PL_debstash) {
ec25cea7 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 }
7dd7d008 165 }
569ac469 166}
167
04a8a223 168char* dd_get_lex_stuff(pTHX) {
96f12726 169 return (DD_HAVE_LEX_STUFF ? SvPVX(PL_lex_stuff) : "");
04a8a223 170}
171
bd85a06e 172void dd_clear_lex_stuff(pTHX) {
96f12726 173 if (DD_HAVE_PARSER)
f05cbc90 174 PL_lex_stuff = (SV*)NULL;
04a8a223 175}
176
177char* dd_get_curstash_name(pTHX) {
178 return HvNAME(PL_curstash);
179}
180
022eb0cc 181int dd_get_linestr_offset(pTHX) {
0da63271 182 char* linestr;
183 if (!DD_HAVE_PARSER) {
184 return -1;
185 }
186 linestr = SvPVX(PL_linestr);
022eb0cc 187 return PL_bufptr - linestr;
188}
189
840ebcbb 190char* dd_move_past_token (pTHX_ char* s) {
923c07a8 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
840ebcbb 204int dd_toke_move_past_token (pTHX_ int offset) {
04a8a223 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
923c07a8 210int 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
f11d21b2 218int dd_toke_scan_ident(pTHX_ int offset) {
219 char tmpbuf[sizeof PL_tokenbuf];
220 char* base_s = SvPVX(PL_linestr) + offset;
f11d21b2 221 char* s = scan_ident(base_s, PL_bufend, tmpbuf, sizeof tmpbuf, 0);
222 return s - base_s;
223}
224
923c07a8 225int dd_toke_scan_str(pTHX_ int offset) {
d3480bb4 226 char* old_pvx = SvPVX(PL_linestr);
08c39634 227 SV* line_copy = sv_2mortal(newSVsv(PL_linestr));
923c07a8 228 char* base_s = SvPVX(PL_linestr) + offset;
229 char* s = scan_str(base_s, FALSE, FALSE);
d3480bb4 230 if(SvPVX(PL_linestr) != old_pvx)
231 croak("PL_linestr reallocated during scan_str, "
232 "Devel::Declare can't continue");
8449c31f 233 if (!s)
234 return 0;
361de2b5 235 if (s <= base_s || memcmp(SvPVX(line_copy), SvPVX(PL_linestr), offset)) {
78bb475d 236 s += SvCUR(line_copy);
86964fb3 237 sv_catsv(line_copy, PL_linestr);
238 dd_set_linestr(aTHX_ SvPV_nolen(line_copy));
86964fb3 239 }
923c07a8 240 return s - base_s;
241}
242
243int dd_toke_skipspace(pTHX_ int offset) {
e8df925b 244 char* old_pvx = SvPVX(PL_linestr);
923c07a8 245 char* base_s = SvPVX(PL_linestr) + offset;
a25db2dc 246 char* s = skipspace_force(base_s);
e8df925b 247 if(SvPVX(PL_linestr) != old_pvx)
248 croak("PL_linestr reallocated during skipspace, "
249 "Devel::Declare can't continue");
923c07a8 250 return s - base_s;
251}
252
023db2fb 253static 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
283static int dd_handle_const(pTHX_ char *name);
284
94caac6e 285/* replacement PL_check rv2cv entry */
286
6e67754a 287STATIC OP *dd_ck_rv2cv(pTHX_ OP *o, void *user_data) {
94caac6e 288 OP* kid;
0ba8c7aa 289 int dd_flags;
94caac6e 290
6f5220b7 291 PERL_UNUSED_VAR(user_data);
292
499109e0 293 if (!DD_AM_LEXING)
294 return o; /* not lexing? */
295
94caac6e 296 if (in_declare) {
023db2fb 297 call_done_declare(aTHX);
94caac6e 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
7dd7d008 306 if (DD_DEBUG_TRACE) {
e81bee92 307 printf("Checking GV %s -> %s\n", HvNAME(GvSTASH(kGVOP_gv)), GvNAME(kGVOP_gv));
308 }
840ebcbb 309
72f20f69 310 dd_flags = dd_is_declarator(aTHX_ GvNAME(kGVOP_gv));
94caac6e 311
72f20f69 312 if (dd_flags == -1)
94caac6e 313 return o;
314
7dd7d008 315 if (DD_DEBUG_TRACE) {
e81bee92 316 printf("dd_flags are: %i\n", dd_flags);
317 printf("PL_tokenbuf: %s\n", PL_tokenbuf);
318 }
840ebcbb 319
023db2fb 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
a9bd9b5e 338 dd_linestr_callback(aTHX_ "rv2cv", GvNAME(kGVOP_gv));
94caac6e 339
53e3ab32 340 return o;
341}
342
6ed8c948 343#if DD_GROW_VIA_BLOCKHOOK
344
345static void dd_block_start(pTHX_ int full)
346{
347 PERL_UNUSED_VAR(full);
e8df925b 348 if (SvLEN(PL_linestr) < DD_PREFERRED_LINESTR_SIZE)
349 (void) lex_grow_linestr(DD_PREFERRED_LINESTR_SIZE);
6ed8c948 350}
351
352#else /* !DD_GROW_VIA_BLOCKHOOK */
353
b7505981 354OP* dd_pp_entereval(pTHX) {
355 dSP;
b7505981 356 STRLEN len;
357 const char* s;
c93bc23e 358 SV *sv;
359#ifdef PERL_5_9_PLUS
ec25cea7 360 SV *saved_hh = NULL;
c93bc23e 361 if (PL_op->op_private & OPpEVAL_HAS_HH) {
362 saved_hh = POPs;
363 }
364#endif
365 sv = POPs;
b7505981 366 if (SvPOK(sv)) {
7dd7d008 367 if (DD_DEBUG_TRACE) {
e81bee92 368 printf("mangling eval sv\n");
369 }
b7505981 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);
53e3ab32 378 }
e8df925b 379 SvGROW(sv, DD_PREFERRED_LINESTR_SIZE);
53e3ab32 380 }
b7505981 381 PUSHs(sv);
c93bc23e 382#ifdef PERL_5_9_PLUS
383 if (PL_op->op_private & OPpEVAL_HAS_HH) {
384 PUSHs(saved_hh);
385 }
386#endif
b7505981 387 return PL_ppaddr[OP_ENTEREVAL](aTHX);
388}
53e3ab32 389
6e67754a 390STATIC OP *dd_ck_entereval(pTHX_ OP *o, void *user_data) {
6f5220b7 391 PERL_UNUSED_VAR(user_data);
392
d8e65fc8 393 if (o->op_ppaddr == PL_ppaddr[OP_ENTEREVAL])
394 o->op_ppaddr = dd_pp_entereval;
94caac6e 395 return o;
396}
397
12b60feb 398#endif /* !DD_GROW_VIA_BLOCKHOOK */
399
6a0bcf35 400static I32 dd_filter_realloc(pTHX_ int idx, SV *sv, int maxlen)
401{
402 const I32 count = FILTER_READ(idx+1, sv, maxlen);
e8df925b 403 SvGROW(sv, DD_PREFERRED_LINESTR_SIZE);
6a0bcf35 404 /* filter_del(dd_filter_realloc); */
405 return count;
406}
407
023db2fb 408static int dd_handle_const(pTHX_ char *name) {
c48ac26f 409 switch (PL_lex_inwhat) {
410 case OP_QR:
411 case OP_MATCH:
412 case OP_SUBST:
413 case OP_TRANS:
f2a41aa1 414 case OP_BACKTICK:
415 case OP_STRINGIFY:
023db2fb 416 return 0;
c48ac26f 417 break;
418 default:
419 break;
8d96afb7 420 }
48ee5c99 421
a25db2dc 422 if (strnEQ(PL_bufptr, "->", 2)) {
023db2fb 423 return 0;
954da332 424 }
425
a25db2dc 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;
c0439e97 435 SV *inject = newSVpvn(SvPVX(PL_linestr), PL_bufptr - SvPVX(PL_linestr));
a25db2dc 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)) {
023db2fb 442 return 0;
a25db2dc 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
a9bd9b5e 452 dd_linestr_callback(aTHX_ "const", name);
bedac9ff 453
023db2fb 454 return 1;
455}
456
457#if !DD_CONST_VIA_RV2CV
458
459STATIC 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
3ea50944 488 return o;
489}
490
023db2fb 491#endif /* !DD_CONST_VIA_RV2CV */
492
8ec78a85 493STATIC void dd_initialize(pTHX) {
494 static int initialized = 0;
495 if (!initialized) {
496 initialized = 1;
6ed8c948 497#if DD_GROW_VIA_BLOCKHOOK
a36413c5 498 {
499 static BHK bhk;
6ed8c948 500#if PERL_VERSION_GE(5,13,6)
a36413c5 501 BhkENTRY_set(&bhk, bhk_start, dd_block_start);
6ed8c948 502#else /* <5.13.6 */
a36413c5 503 BhkENTRY_set(&bhk, start, dd_block_start);
6ed8c948 504#endif /* <5.13.6 */
a36413c5 505 Perl_blockhook_register(aTHX_ &bhk);
506 }
6ed8c948 507#else /* !DD_GROW_VIA_BLOCKHOOK */
6e67754a 508 hook_op_check(OP_ENTEREVAL, dd_ck_entereval, NULL);
6ed8c948 509#endif /* !DD_GROW_VIA_BLOCKHOOK */
510 hook_op_check(OP_RV2CV, dd_ck_rv2cv, NULL);
023db2fb 511#if !DD_CONST_VIA_RV2CV
6e67754a 512 hook_op_check(OP_CONST, dd_ck_const, NULL);
023db2fb 513#endif /* !DD_CONST_VIA_RV2CV */
94caac6e 514 }
8ec78a85 515}
516
517MODULE = Devel::Declare PACKAGE = Devel::Declare
518
519PROTOTYPES: DISABLE
520
521void
522initialize()
523 CODE:
524 dd_initialize(aTHX);
525
526void
527setup()
528 CODE:
529 dd_initialize(aTHX);
530 filter_add(dd_filter_realloc, NULL);
569ac469 531
532char*
533get_linestr()
534 CODE:
535 RETVAL = dd_get_linestr(aTHX);
536 OUTPUT:
537 RETVAL
923c07a8 538
539void
540set_linestr(char* new_value)
541 CODE:
542 dd_set_linestr(aTHX_ new_value);
543
04a8a223 544char*
545get_lex_stuff()
546 CODE:
547 RETVAL = dd_get_lex_stuff(aTHX);
548 OUTPUT:
549 RETVAL
550
551void
552clear_lex_stuff()
553 CODE:
554 dd_clear_lex_stuff(aTHX);
555
556char*
557get_curstash_name()
558 CODE:
559 RETVAL = dd_get_curstash_name(aTHX);
560 OUTPUT:
561 RETVAL
562
923c07a8 563int
022eb0cc 564get_linestr_offset()
565 CODE:
566 RETVAL = dd_get_linestr_offset(aTHX);
567 OUTPUT:
568 RETVAL
569
570int
923c07a8 571toke_scan_word(int offset, int handle_package)
572 CODE:
573 RETVAL = dd_toke_scan_word(aTHX_ offset, handle_package);
574 OUTPUT:
575 RETVAL
576
577int
04a8a223 578toke_move_past_token(int offset);
579 CODE:
580 RETVAL = dd_toke_move_past_token(aTHX_ offset);
581 OUTPUT:
582 RETVAL
583
8449c31f 584SV*
923c07a8 585toke_scan_str(int offset);
8449c31f 586 PREINIT:
587 int len;
923c07a8 588 CODE:
8449c31f 589 len = dd_toke_scan_str(aTHX_ offset);
590 RETVAL = len ? newSViv(len) : &PL_sv_undef;
923c07a8 591 OUTPUT:
592 RETVAL
593
594int
f11d21b2 595toke_scan_ident(int offset)
596 CODE:
597 RETVAL = dd_toke_scan_ident(aTHX_ offset);
598 OUTPUT:
599 RETVAL
600
601int
923c07a8 602toke_skipspace(int offset)
603 CODE:
604 RETVAL = dd_toke_skipspace(aTHX_ offset);
605 OUTPUT:
606 RETVAL
04a8a223 607
608int
609get_in_declare()
610 CODE:
611 RETVAL = in_declare;
612 OUTPUT:
613 RETVAL
614
615void
616set_in_declare(int value)
617 CODE:
618 in_declare = value;
e81bee92 619
620BOOT:
0a3b37d1 621{
87195072 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 }
e81bee92 629 }
0a3b37d1 630}