1 #define NEED_newSVpvn_flags_GLOBAL
2 #define NEED_newSVpvn_share
3 #define NEED_newSV_type
4 #include "xslate.h"
5 
6 #include "uri_unsafe.h"
7 
8 /* aliases */
9 #define TXCODE_literal_i   TXCODE_literal
10 #define TXCODE_depend      TXCODE_noop
11 #define TXCODE_macro_begin TXCODE_noop
12 #define TXCODE_macro_nargs TXCODE_noop
13 #define TXCODE_macro_outer TXCODE_noop
14 #define TXCODE_set_opinfo  TXCODE_noop
15 #define TXCODE_super       TXCODE_noop
16 
17 #include "xslate_ops.h"
18 
19 static bool dump_load = FALSE;
20 
21 #ifdef DEBUGGING
22 #define TX_st_sa  *tx_sv_safe(aTHX_ &(TX_st->sa),  "TX_st->sa",  __FILE__, __LINE__)
23 #define TX_st_sb  *tx_sv_safe(aTHX_ &(TX_st->sb),  "TX_st->sb",  __FILE__, __LINE__)
24 static SV**
tx_sv_safe(pTHX_ SV ** const svp,const char * const name,const char * const f,int const l)25 tx_sv_safe(pTHX_ SV** const svp, const char* const name, const char* const f, int const l) {
26     if(*svp == NULL) {
27         croak("[BUG] %s is NULL at %s line %d.\n", name, f, l);
28     }
29     return svp;
30 }
31 
32 #define TX_lvarx_get(st, ix) tx_lvar_get_safe(aTHX_ st, ix)
33 
34 static SV*
tx_lvar_get_safe(pTHX_ tx_state_t * const st,I32 const lvar_ix)35 tx_lvar_get_safe(pTHX_ tx_state_t* const st, I32 const lvar_ix) {
36     AV* const cframe  = TX_current_framex(st);
37     I32 const real_ix = lvar_ix + TXframe_START_LVAR;
38 
39     assert(SvTYPE(cframe) == SVt_PVAV);
40     if(AvFILLp(cframe) < real_ix) {
41         croak("[BUG] Refers to unallocated local variable %d (> %d)",
42             (int)lvar_ix, (int)(AvFILLp(cframe) - TXframe_START_LVAR));
43     }
44 
45     if(!st->pad) {
46         croak("[BUG] Refers to local variable %d before initialization",
47             (int)lvar_ix);
48     }
49     return st->pad[lvar_ix];
50 }
51 
52 
53 #else /* DEBUGGING */
54 #define TX_st_sa        (TX_st->sa)
55 #define TX_st_sb        (TX_st->sb)
56 
57 #define TX_lvarx_get(st, ix) ((st)->pad[ix])
58 #endif /* DEBUGGING */
59 
60 #define TX_op_arg    (TX_op->u_arg)
61 #define TX_op_arg_sv (TX_op_arg.sv)
62 #define TX_op_arg_iv (TX_op_arg.iv)
63 #define TX_op_arg_pc (TX_op_arg.pc)
64 
65 #define TX_lvarx(st, ix) tx_load_lvar(aTHX_ st, ix)
66 
67 #define TX_lvar(ix)     TX_lvarx(TX_st, ix)     /* init if uninitialized */
68 #define TX_lvar_get(ix) TX_lvarx_get(TX_st, ix)
69 
70 #define TX_ckuuv_lhs(x) tx_sv_check_uuv(aTHX_ (x), "lhs")
71 #define TX_ckuuv_rhs(x) tx_sv_check_uuv(aTHX_ (x), "rhs")
72 
73 #define TX_UNMARK_RAW(sv) SvRV(sv)
74 
75 #define MY_CXT_KEY "Text::Xslate::_guts" XS_VERSION
76 typedef struct {
77     I32 depth;
78     HV* raw_stash;
79     HV* macro_stash;
80 
81     tx_state_t* current_st; /* set while tx_execute(), othewise NULL */
82 
83     /* those handlers are just \&_warn and \&_die,
84        but stored here for performance */
85     SV* warn_handler;
86     SV* die_handler;
87 
88     /* original error handlers */
89     SV* orig_warn_handler;
90     SV* orig_die_handler;
91     SV* make_error;
92 } my_cxt_t;
93 START_MY_CXT
94 
95 static void
tx_sv_clear(pTHX_ SV * const sv)96 tx_sv_clear(pTHX_ SV* const sv) {
97     sv_unmagic(sv, PERL_MAGIC_taint);
98     sv_setsv(sv, NULL);
99 }
100 
101 const char*
102 tx_neat(pTHX_ SV* const sv);
103 
104 static SV*
105 tx_load_lvar(pTHX_ tx_state_t* const st, I32 const lvar_ix);
106 
107 static AV*
108 tx_push_frame(pTHX_ tx_state_t* const st);
109 
110 static void
111 tx_pop_frame(pTHX_ tx_state_t* const st, bool const replace_output);
112 
113 static SV*
114 tx_funcall(pTHX_ tx_state_t* const st, SV* const func, const char* const name);
115 
116 static SV*
117 tx_fetch(pTHX_ tx_state_t* const st, SV* const var, SV* const key);
118 
119 static SV*
120 tx_sv_to_ref(pTHX_ SV* const sv, svtype const svt, int const amg_id);
121 
122 int
tx_sv_is_array_ref(pTHX_ SV * const sv)123 tx_sv_is_array_ref(pTHX_ SV* const sv) {
124     assert(sv);
125     return SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV && !SvOBJECT(SvRV(sv));
126 }
127 
128 int
tx_sv_is_hash_ref(pTHX_ SV * const sv)129 tx_sv_is_hash_ref(pTHX_ SV* const sv) {
130     assert(sv);
131     return SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV && !SvOBJECT(SvRV(sv));
132 }
133 
134 int
tx_sv_is_code_ref(pTHX_ SV * const sv)135 tx_sv_is_code_ref(pTHX_ SV* const sv) {
136     assert(sv);
137     return SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV && !SvOBJECT(SvRV(sv));
138 }
139 
140 SV*
tx_merge_hash(pTHX_ tx_state_t * const st,SV * base,SV * value)141 tx_merge_hash(pTHX_ tx_state_t* const st, SV* base, SV* value) {
142     HV* const hv        = (HV*)SvRV(base);
143     HV* const result    = newHVhv(hv);
144     SV* const resultref = newRV_noinc((SV*)result);
145     HE* he;
146     HV* m;
147     sv_2mortal((SV*)resultref);
148 
149     SvGETMAGIC(base);
150     SvGETMAGIC(value);
151 
152     if(!tx_sv_is_hash_ref(aTHX_ value)) {
153         if (st) {
154             tx_error(aTHX_ st, "Merging value is not a HASH reference");
155         }
156         else {
157             Perl_croak(aTHX_ "Merging value is not a HASH reference");
158         }
159         return resultref;
160     }
161 
162     m = (HV*)SvRV(value);
163 
164     hv_iterinit(m);
165     while((he = hv_iternext(m))) {
166         (void)hv_store_ent(result,
167             hv_iterkeysv(he),
168             newSVsv(hv_iterval(hv, he)),
169             0U);
170     }
171 
172     return resultref;
173 }
174 
175 STATIC_INLINE bool
176 tx_str_is_raw(pTHX_ pMY_CXT_ SV* const sv); /* doesn't handle magics */
177 
178 STATIC_INLINE void
179 tx_sv_cat(pTHX_ SV* const dest, SV* const src);
180 
181 static void
182 tx_sv_cat_with_html_escape_force(pTHX_ SV* const dest, SV* const src);
183 
184 STATIC_INLINE void
185 tx_print(pTHX_ tx_state_t* const st, SV* const sv);
186 
187 static SV*
188 tx_html_escape(pTHX_ SV* const str);
189 
190 static SV*
191 tx_uri_escape(pTHX_ SV* const src);
192 
193 STATIC_INLINE I32
194 tx_sv_eq(pTHX_ SV* const a, SV* const b);
195 
196 static SV*
197 tx_sv_check_uuv(pTHX_ SV* const sv, const char* const name);
198 
199 static I32
200 tx_sv_match(pTHX_ SV* const a, SV* const b);
201 
202 static bool
203 tx_sv_is_macro(pTHX_ SV* const sv);
204 
205 static void
206 tx_macro_enter(pTHX_ tx_state_t* const txst, AV* const macro, tx_pc_t const retaddr);
207 
208 static void
209 tx_execute(pTHX_ pMY_CXT_ tx_state_t* const base, SV* const output, HV* const hv);
210 
211 static tx_state_t*
212 tx_load_template(pTHX_ SV* const self, SV* const name, bool const from_include);
213 
214 #ifndef save_op
215 #define save_op() my_save_op(aTHX)
216 static void
my_save_op(pTHX)217 my_save_op(pTHX) { /* copied from scope.c */
218     SSCHECK(2);
219     SSPUSHPTR(PL_op);
220     SSPUSHINT(SAVEt_OP);
221 }
222 #endif
223 
224 #include "src/xslate_opcode.inc"
225 
226 const char*
tx_neat(pTHX_ SV * const sv)227 tx_neat(pTHX_ SV* const sv) {
228     if(SvOK(sv)) {
229         if(SvROK(sv) || looks_like_number(sv) || isGV(sv)) {
230             return form("%"SVf, sv);
231         }
232         else {
233             return form("'%"SVf"'", sv);
234         }
235     }
236     return "nil";
237 }
238 
239 static IV
tx_verbose(pTHX_ tx_state_t * const st)240 tx_verbose(pTHX_ tx_state_t* const st) {
241     HV* const hv = (HV*)SvRV(st->engine);
242     SV* const sv = *hv_fetchs(hv, "verbose", TRUE);
243     return SvIV(sv);
244 }
245 
246 
247 static void
tx_call_error_handler(pTHX_ SV * const handler,SV * const msg)248 tx_call_error_handler(pTHX_ SV* const handler, SV* const msg) {
249     dSP;
250     PUSHMARK(SP);
251     XPUSHs(msg);
252     PUTBACK;
253     call_sv(handler, G_VOID | G_DISCARD);
254 }
255 
256 /* for trivial errors, ignored by default */
257 void
tx_warn(pTHX_ tx_state_t * const st,const char * const fmt,...)258 tx_warn(pTHX_ tx_state_t* const st, const char* const fmt, ...) {
259     assert(st);
260     assert(fmt);
261     if(tx_verbose(aTHX_ st) > TX_VERBOSE_DEFAULT) { /* stronger than the default */
262         dMY_CXT;
263         SV* msg;
264         va_list args;
265         va_start(args, fmt);
266 
267         ENTER;
268         SAVETMPS;
269         msg = sv_2mortal( vnewSVpvf(fmt, &args) );
270         tx_call_error_handler(aTHX_ MY_CXT.warn_handler, msg);
271         va_end(args);
272         FREETMPS;
273         LEAVE;
274     }
275 }
276 
277 /* for severe errors, warned by default */
278 void
tx_error(pTHX_ tx_state_t * const st,const char * const fmt,...)279 tx_error(pTHX_ tx_state_t* const st, const char* const fmt, ...) {
280     assert(st);
281     assert(fmt);
282     if(tx_verbose(aTHX_ st) >= TX_VERBOSE_DEFAULT) { /* equal or stronger than the default */
283         dMY_CXT;
284         SV* msg;
285         va_list args;
286         va_start(args, fmt);
287         msg = sv_2mortal( vnewSVpvf(fmt, &args) );
288         tx_call_error_handler(aTHX_ MY_CXT.warn_handler, msg);
289         /* not reached */
290         va_end(args);
291     }
292 }
293 
294 static SV* /* allocate and load a lexcal variable */
tx_load_lvar(pTHX_ tx_state_t * const st,I32 const lvar_ix)295 tx_load_lvar(pTHX_ tx_state_t* const st, I32 const lvar_ix) { /* the guts of TX_lvar() */
296     AV* const cframe  = TX_current_framex(st);
297     I32 const real_ix = lvar_ix + TXframe_START_LVAR;
298 
299     assert(SvTYPE(cframe) == SVt_PVAV);
300 
301     if(AvFILLp(cframe) < real_ix
302        || AvARRAY(cframe)[real_ix] == NULL
303        || SvREADONLY(AvARRAY(cframe)[real_ix])) {
304         av_store(cframe, real_ix, newSV(0));
305     }
306     st->pad = AvARRAY(cframe) + TXframe_START_LVAR;
307 
308     return TX_lvarx_get(st, lvar_ix);
309 }
310 
311 static AV*
tx_push_frame(pTHX_ tx_state_t * const st)312 tx_push_frame(pTHX_ tx_state_t* const st) {
313     AV* newframe;
314 
315     if(st->current_frame > TX_MAX_DEPTH) {
316         croak("Macro call is too deep (> %d)", TX_MAX_DEPTH);
317     }
318     st->current_frame++;
319 
320     newframe = (AV*)*av_fetch(st->frames, st->current_frame, TRUE);
321 
322     (void)SvUPGRADE((SV*)newframe, SVt_PVAV);
323     if(AvFILLp(newframe) < TXframe_START_LVAR) {
324         av_extend(newframe, TXframe_START_LVAR);
325     }
326     /* switch the pad */
327     st->pad = AvARRAY(newframe) + TXframe_START_LVAR;
328     return newframe;
329 }
330 
331 static void
tx_pop_frame(pTHX_ tx_state_t * const st,bool const replace_output)332 tx_pop_frame(pTHX_ tx_state_t* const st, bool const replace_output) {
333     AV* const top  = TX_frame_at(st, st->current_frame);
334 
335     av_fill(top, TXframe_START_LVAR - 1);
336 
337     assert( st->current_frame >= 0 );
338     if (--st->current_frame >= 0) {
339         /* switch the pad */
340         st->pad = AvARRAY(TX_frame_at(st, st->current_frame))
341                     + TXframe_START_LVAR;
342     }
343 
344     if(replace_output) {
345         SV** const ary      = AvARRAY(top);
346         SV* const tmp       = ary[TXframe_OUTPUT];
347         ary[TXframe_OUTPUT] = st->output;
348         st->output          = tmp;
349     }
350 }
351 
352 SV* /* thin wrapper of Perl_call_sv() */
tx_call_sv(pTHX_ tx_state_t * const st,SV * const sv,I32 const flags,const char * const name)353 tx_call_sv(pTHX_ tx_state_t* const st, SV* const sv, I32 const flags, const char* const name) {
354     SV* retval;
355     call_sv(sv, G_SCALAR | G_EVAL | flags);
356     retval = TX_pop();
357     if(TX_CATCH_ERROR()) {
358         tx_error(aTHX_ st, "%"SVf "\n"
359             "\t... exception caught on %s", ERRSV, name);
360     }
361     return retval;
362 }
363 
364 static SV*
tx_funcall(pTHX_ tx_state_t * const st,SV * const func,const char * const name)365 tx_funcall(pTHX_ tx_state_t* const st, SV* const func, const char* const name) {
366     HV* dummy_stash;
367     GV* dummy_gv;
368     CV* cv;
369     SV* retval;
370     SvGETMAGIC(func);
371 
372     if(UNLIKELY(!SvOK(func))) {
373         dTX_optable;
374         tx_code_t* const c = st->pc - 1;
375         (void)POPMARK;
376         tx_error(aTHX_ st, "Undefined function%s is called on %s",
377             c->exec_code == tx_optable[TXOP_fetch_s]
378                 ? form(" %"SVf"()", c->u_arg.sv)
379                 : "", name);
380         retval = NULL;
381         goto finish;
382     }
383 
384     cv = sv_2cv(func, &dummy_stash, &dummy_gv, FALSE);
385 
386     if(UNLIKELY(!cv)) {
387         (void)POPMARK;
388         tx_error(aTHX_ st, "Functions must be a CODE reference, not %s",
389             tx_neat(aTHX_ func));
390         retval = NULL;
391         goto finish;
392     }
393 
394     retval = tx_call_sv(aTHX_ st, (SV*)cv, 0, "function call");
395 
396     finish:
397     sv_setsv_nomg(st->targ, retval);
398 
399     return st->targ;
400 }
401 
402 static SV*
tx_fetch(pTHX_ tx_state_t * const st,SV * const var,SV * const key)403 tx_fetch(pTHX_ tx_state_t* const st, SV* const var, SV* const key) {
404     SV* retval;
405 
406     SvGETMAGIC(var);
407     if(SvROK(var) && SvOBJECT(SvRV(var))) {
408         dSP;
409         PUSHMARK(SP);
410         XPUSHs(var);
411         PUTBACK;
412 
413         return tx_call_sv(aTHX_ st, key, G_METHOD, "accessor");
414     }
415 
416     retval = NULL;
417     if(SvROK(var)){
418         SV* const rv = SvRV(var);
419         SvGETMAGIC(key);
420         if(SvTYPE(rv) == SVt_PVHV) {
421             if(SvOK(key)) {
422                 HE* const he = hv_fetch_ent((HV*)rv, key, FALSE, 0U);
423                 if(he) {
424                     retval = hv_iterval((HV*)rv, he);
425                 }
426             }
427             else {
428                 tx_warn(aTHX_ st, "Use of nil as a field key");
429             }
430         }
431         else if(SvTYPE(rv) == SVt_PVAV) {
432             if(LooksLikeNumber(key)) {
433                 SV** const svp = av_fetch((AV*)rv, SvIV(key), FALSE);
434                 if(svp) {
435                     retval = *svp;
436                 }
437             }
438             else {
439                 tx_warn(aTHX_ st, "Use of %s as an array index",
440                     tx_neat(aTHX_ key));
441             }
442         }
443         else {
444             goto invalid_container;
445         }
446     }
447     else if(SvOK(var)){ /* string, number, etc. */
448         invalid_container:
449         tx_error(aTHX_ st, "Cannot access %s (%s is not a container)",
450             tx_neat(aTHX_ key), tx_neat(aTHX_ var));
451     }
452     else { /* undef */
453         tx_warn(aTHX_ st, "Use of nil to access %s", tx_neat(aTHX_ key));
454     }
455     TAINT_NOT;
456 
457     return retval ? retval : &PL_sv_undef;
458 }
459 
460 #ifndef amagic_deref_call
461 #define amagic_deref_call(ref, method) my_amagic_deref_call(aTHX_ ref, method)
462 /* portability */
463 static SV*
my_amagic_deref_call(pTHX_ SV * ref,const int method)464 my_amagic_deref_call(pTHX_ SV* ref, const int method) {
465     SV* tmpsv = NULL;
466 
467     while (SvAMAGIC(ref) &&
468        (tmpsv = amagic_call(ref, &PL_sv_undef, method,
469                 AMGf_noright | AMGf_unary))) {
470         if (!SvROK(tmpsv))
471             Perl_croak(aTHX_ "Overloaded dereference did not return a reference");
472         if (tmpsv == ref || SvRV(tmpsv) == SvRV(ref)) {
473             /* Bail out if it returns us the same reference.  */
474             return tmpsv;
475         }
476         ref = tmpsv;
477     }
478     return tmpsv ? tmpsv : ref;
479 }
480 #endif
481 
482 static SV*
tx_sv_to_ref(pTHX_ SV * const sv,svtype const svt,const int amg_id)483 tx_sv_to_ref(pTHX_ SV* const sv, svtype const svt, const int amg_id) {
484     if(SvROK(sv)) {
485         SV* const r = SvRV(sv);
486         if(SvOBJECT(r)) {
487             if(SvAMAGIC(sv)) {
488                 SV* const tmpsv = amagic_deref_call(sv, amg_id);
489                 if(SvROK(tmpsv)
490                         && SvTYPE(SvRV(tmpsv)) == svt
491                         && !SvOBJECT(SvRV(tmpsv))) {
492                     return tmpsv;
493                 }
494             }
495         }
496         else if(SvTYPE(r) == svt) {
497             return sv;
498         }
499     }
500     return NULL;
501 }
502 
503 STATIC_INLINE bool
tx_str_is_raw(pTHX_ pMY_CXT_ SV * const sv)504 tx_str_is_raw(pTHX_ pMY_CXT_ SV* const sv) {
505     if(SvROK(sv) && SvOBJECT(SvRV(sv))) {
506         return SvTYPE(SvRV(sv)) <= SVt_PVMG
507             && SvSTASH(SvRV(sv)) == MY_CXT.raw_stash;
508     }
509     return FALSE;
510 }
511 
512 SV*
tx_mark_raw(pTHX_ SV * const str)513 tx_mark_raw(pTHX_ SV* const str) {
514     dMY_CXT;
515     SvGETMAGIC(str);
516     if(!SvOK(str)) {
517         return str;
518     }
519     else if(tx_str_is_raw(aTHX_ aMY_CXT_ str)) {
520         return str;
521     }
522     else {
523         SV* const sv = newSV_type(SVt_PVMG);
524         sv_setsv(sv, str);
525         return sv_2mortal(sv_bless(newRV_noinc(sv), MY_CXT.raw_stash));
526     }
527 }
528 
529 SV*
tx_unmark_raw(pTHX_ SV * const str)530 tx_unmark_raw(pTHX_ SV* const str) {
531     dMY_CXT;
532     SvGETMAGIC(str);
533     if(tx_str_is_raw(aTHX_ aMY_CXT_ str)) {
534         return TX_UNMARK_RAW(str);
535     }
536     else {
537         return str;
538     }
539 }
540 
541 /* does sv_catsv_nomg(dest, src), but significantly faster */
542 STATIC_INLINE void
tx_sv_cat(pTHX_ SV * const dest,SV * const src)543 tx_sv_cat(pTHX_ SV* const dest, SV* const src) {
544     STRLEN len;
545     const char* pv = SvPV_const(src, len);
546 
547     if(!SvUTF8(dest) && SvUTF8(src)) {
548         sv_utf8_upgrade(dest);
549     }
550 
551     if(SvUTF8(dest) == SvUTF8(src)
552        || is_utf8_string((const U8 *)pv, len)) {
553         STRLEN const dest_cur = SvCUR(dest);
554         char* const d         = SvGROW(dest, dest_cur + len + 1 /* count '\0' */);
555 
556         SvCUR_set(dest, dest_cur + len);
557         Copy(pv, d + dest_cur, len + 1 /* copy '\0' */, char);
558     }
559     else {
560         STRLEN const dest_cur = SvCUR(dest);
561         /* Longest UTF-8 representation of each char is 2 octets. */
562         char* const d_start   = SvGROW(dest, dest_cur + 2 * len + 1 /* count '\0' */);
563         char* d = d_start + dest_cur;
564 
565         while(len--) {
566             const U8 c = *pv++;
567             if (UTF8_IS_INVARIANT(c)) {
568                 *(d++) = c;
569             } else {
570                 *(d++) = UTF8_EIGHT_BIT_HI(c);
571                 *(d++) = UTF8_EIGHT_BIT_LO(c);
572             }
573         }
574         *d = '\0';
575         SvCUR_set(dest, d - d_start);
576     }
577 }
578 
579 static void /* doesn't care about raw-ness */
tx_sv_cat_with_html_escape_force(pTHX_ SV * const dest,SV * const src)580 tx_sv_cat_with_html_escape_force(pTHX_ SV* const dest, SV* const src) {
581     STRLEN len;
582     const char*       cur = SvPV_const(src, len);
583     const char* const end = cur + len;
584     STRLEN const dest_cur = SvCUR(dest);
585     char* d;
586     const U32 upgrade_on_copy = SvUTF8(dest) && !SvUTF8(src)
587         && !is_utf8_string((const U8 *)cur, len);
588 
589     (void)SvGROW(dest, dest_cur + ( len * ( sizeof("&quot;") - 1) ) + 1);
590     if(!SvUTF8(dest) && SvUTF8(src)) {
591         sv_utf8_upgrade(dest);
592     }
593 
594     d = SvPVX(dest) + dest_cur;
595 
596 #define CopyToken(token, to) STMT_START {          \
597         Copy(token "", to, sizeof(token)-1, char); \
598         to += sizeof(token)-1;                     \
599     } STMT_END
600 
601     while(cur != end) {
602         const char c = *(cur++);
603         if(c == '&') {
604             CopyToken("&amp;", d);
605         }
606         else if(c == '<') {
607             CopyToken("&lt;", d);
608         }
609         else if(c == '>') {
610             CopyToken("&gt;", d);
611         }
612         else if(c == '"') {
613             CopyToken("&quot;", d);
614         }
615         else if(c == '\'') {
616             // XXX: Internet Explorer (at least version 8) doesn't support &apos; in title
617             // CopyToken("&apos;", d);
618             CopyToken("&#39;", d);
619         }
620         else if (upgrade_on_copy && !UTF8_IS_INVARIANT(c)) {
621             *(d++) = UTF8_EIGHT_BIT_HI((U8) c);
622             *(d++) = UTF8_EIGHT_BIT_LO((U8) c);
623         }
624         else {
625             *(d++) = c;
626         }
627     }
628 
629 #undef CopyToken
630 
631     SvCUR_set(dest, d - SvPVX(dest));
632     *SvEND(dest) = '\0';
633 }
634 
635 STATIC_INLINE void
tx_print(pTHX_ tx_state_t * const st,SV * const sv)636 tx_print(pTHX_ tx_state_t* const st, SV* const sv) {
637     dMY_CXT;
638     SV* const out = st->output;
639 
640     SvGETMAGIC(sv);
641     if(tx_str_is_raw(aTHX_ aMY_CXT_ sv)) {
642         SV* const arg = TX_UNMARK_RAW(sv);
643         if(SvOK(arg)) {
644             tx_sv_cat(aTHX_ out, arg);
645         }
646         else {
647             tx_warn(aTHX_ st, "Use of nil to print");
648         }
649     }
650     else if(SvOK(sv)) {
651         tx_sv_cat_with_html_escape_force(aTHX_ out, sv);
652     }
653     else {
654         tx_warn(aTHX_ st, "Use of nil to print");
655         /* does nothing */
656     }
657 }
658 
659 static SV*
tx_html_escape(pTHX_ SV * const str)660 tx_html_escape(pTHX_ SV* const str) {
661     dMY_CXT;
662     SvGETMAGIC(str);
663     if(!( !SvOK(str) || tx_str_is_raw(aTHX_ aMY_CXT_ str) )) {
664         SV* const dest = newSVpvs_flags("", SVs_TEMP);
665         tx_sv_cat_with_html_escape_force(aTHX_ dest, str);
666         return tx_mark_raw(aTHX_ dest);
667     }
668     else {
669         return str;
670     }
671 }
672 
673 
674 static SV*
tx_uri_escape(pTHX_ SV * const src)675 tx_uri_escape(pTHX_ SV* const src) {
676     /* TODO:
677         Currently it is encoded to UTF-8, but
678         the output encoding can be specified in a future (?).
679      */
680 
681     SvGETMAGIC(src);
682     if(SvOK(src)) {
683         STRLEN len;
684         const char* pv        = SvPV_const(src, len);
685         const char* const end = pv + len;
686         SV* const dest = sv_newmortal();
687         sv_grow(dest, len * 2); /* just a hint; upgrading Svt_PV */
688         SvPOK_on(dest);
689 
690         while(pv != end) {
691             if(is_uri_unsafe(*pv)) {
692                 /* identical to PL_hexdigit + 16 */
693                 static const char hexdigit[] = "0123456789ABCDEF";
694                 char p[3];
695                 p[0] = '%';
696                 p[1] = hexdigit[((U8)*pv & 0xF0) >> 4]; /* high 4 bits */
697                 p[2] = hexdigit[((U8)*pv & 0x0F)];      /* low  4 bits */
698                 sv_catpvn(dest, p, 3);
699             }
700             else {
701                 sv_catpvn(dest, pv, 1);
702             }
703             pv++;
704         }
705         return dest;
706     }
707     else {
708         return &PL_sv_undef;
709     }
710 }
711 
712 /* for tx_ckuuv_lhs() / tx_ckuuv_rhs() macros */
713 static SV*
tx_sv_check_uuv(pTHX_ SV * const sv,const char * const name)714 tx_sv_check_uuv(pTHX_ SV* const sv, const char* const name) {
715     /* check "Use of uninitialized value" (uuv) */
716     SvGETMAGIC(sv);
717     if(!SvOK(sv)) {
718         dMY_CXT;
719         tx_warn(aTHX_ MY_CXT.current_st,
720             "Use of nil for %s of binary operator", name);
721         return &PL_sv_no;
722     }
723     return sv;
724 }
725 
726 static I32
tx_sv_eq_nomg(pTHX_ SV * const a,SV * const b)727 tx_sv_eq_nomg(pTHX_ SV* const a, SV* const b) {
728     if(SvOK(a)) {
729         if(SvOK(b)) {
730             U32 const af = (SvFLAGS(a) & (SVf_POK|SVf_IOK|SVf_NOK));
731             U32 const bf =  SvFLAGS(b) & af;
732             return bf == SVf_IOK
733                 ? SvIVX(a) == SvIVX(b)
734                 : sv_eq(a, b);
735         }
736         else {
737             return FALSE;
738         }
739     }
740     else { /* !SvOK(a) */
741         return !SvOK(b);
742     }
743 }
744 
745 STATIC_INLINE I32
tx_sv_eq(pTHX_ SV * const a,SV * const b)746 tx_sv_eq(pTHX_ SV* const a, SV* const b) {
747     SvGETMAGIC(a);
748     SvSETMAGIC(b);
749     return tx_sv_eq_nomg(aTHX_ a, b);
750 }
751 
752 static I32
tx_sv_match(pTHX_ SV * const a,SV * const b)753 tx_sv_match(pTHX_ SV* const a, SV* const b) {
754     SvGETMAGIC(a);
755     SvGETMAGIC(b);
756 
757     if(SvROK(b)) {
758         SV* const r = SvRV(b);
759         if(SvOBJECT(r)) { /* a ~~ $object */
760             /* XXX: what I should do? */
761             return tx_sv_eq_nomg(aTHX_ a, b);
762         }
763         else if(SvTYPE(r) == SVt_PVAV) { /* a ~~ [ ... ] */
764             AV* const av  = (AV*)r;
765             I32 const len = av_len(av) + 1;
766             I32 i;
767             for(i = 0; i < len; i++) {
768                 SV** const svp = av_fetch(av, i, FALSE);
769                 SV* item;
770                 if(svp) {
771                     item = *svp;
772                     SvGETMAGIC(item);
773                 }
774                 else {
775                     item = &PL_sv_undef;
776                 }
777                 if(tx_sv_eq_nomg(aTHX_ a, item)) {
778                     return TRUE;
779                 }
780             }
781             return FALSE;
782         }
783         else if(SvTYPE(r) == SVt_PVHV) { /* a ~~ { ... } */
784             if(SvOK(a)) {
785                 HV* const hv = (HV*)r;
786                 return hv_exists_ent(hv, a, 0U);
787             }
788             else {
789                 return FALSE;
790             }
791         }
792         /* fallthrough */
793     }
794 
795     return tx_sv_eq_nomg(aTHX_ a, b);
796 }
797 
798 static bool
tx_sv_is_macro(pTHX_ SV * const sv)799 tx_sv_is_macro(pTHX_ SV* const sv) {
800 
801     if(sv_isobject(sv)) {
802         AV* const macro = (AV*)SvRV(sv);
803         dMY_CXT;
804         if(SvSTASH(macro) == MY_CXT.macro_stash) {
805             if(!(SvTYPE(macro) == SVt_PVAV && AvFILLp(macro) == (TXm_size - 1))) {
806                 croak("Oops: Invalid macro object");
807             }
808             return TRUE;
809         }
810     }
811     return FALSE;
812 }
813 
814 XS(XS_Text__Xslate__macrocall); /* -Wmissing-prototype */
XS(XS_Text__Xslate__macrocall)815 XS(XS_Text__Xslate__macrocall){
816     dVAR; dSP; /* macrocall routine do dMARK, so we don't it here */
817     dMY_CXT;
818     SV* const macro = (SV*)CvXSUBANY(cv).any_ptr;
819     if(!(MY_CXT.current_st && macro)) {
820         croak("Macro is not callable outside of templates");
821     }
822     XPUSHs( tx_proccall(aTHX_ MY_CXT.current_st, macro, "macro") );
823     PUTBACK;
824     return;
825 }
826 
827 /* called by tx_methodcall() */
828 /* proc may be a Xslate macro or a Perl subroutine (code ref) */
829 SV*
tx_proccall(pTHX_ tx_state_t * const txst,SV * const proc,const char * const name)830 tx_proccall(pTHX_ tx_state_t* const txst, SV* const proc, const char* const name) {
831     if(tx_sv_is_macro(aTHX_ proc)) {
832         dTX_optable;
833         tx_pc_t const save_pc = TX_st->pc;
834         tx_code_t proc_end;
835 
836         proc_end.exec_code = tx_optable[ TXOP_end ];
837         tx_macro_enter(aTHX_ TX_st, (AV*)SvRV(proc), &proc_end);
838         TX_RUNOPS(TX_st);
839         /* after tx_macro_end */
840 
841         TX_st->pc = save_pc;
842         //warn("# return from %s\n", name);
843 
844         return TX_st_sa;
845     }
846     else if (tx_sv_is_code_ref(aTHX_ proc) && CvXSUB((CV*)SvRV(proc)) == XS_Text__Xslate__macrocall) {
847         /* macro wrapper XSUB created by Text::Xslate::Type::as_code_ref() */
848         SV* const m = CvXSUBANY((CV*)SvRV(proc)).any_ptr;
849         sv_dump(proc);
850         sv_dump(m);
851         Perl_croak(aTHX_ "xxx");
852         return tx_proccall(aTHX_ TX_st, m, name);
853     }
854     else {
855         return tx_funcall(aTHX_ TX_st, proc, name);
856     }
857 }
858 
859 
860 static void
tx_macro_enter(pTHX_ tx_state_t * const txst,AV * const macro,tx_pc_t const retaddr)861 tx_macro_enter(pTHX_ tx_state_t* const txst, AV* const macro, tx_pc_t const retaddr) {
862     dSP;
863     dMARK;
864     I32 const items    = SP - MARK;
865     SV* const name     = AvARRAY(macro)[TXm_NAME];
866     tx_pc_t const addr = INT2PTR(tx_pc_t, SvUVX(AvARRAY(macro)[TXm_ADDR]));
867     IV const nargs     = SvIVX(AvARRAY(macro)[TXm_NARGS]);
868     UV const outer     = SvUVX(AvARRAY(macro)[TXm_OUTER]);
869     AV* cframe; /* new frame */
870     UV i;
871     SV* tmp;
872 
873     if(items != nargs) {
874         tx_error(aTHX_ TX_st, "Wrong number of arguments for %"SVf" (%d %c %d)",
875             name, (int)items, items > nargs ? '>' : '<', (int)nargs);
876         TX_st->sa = &PL_sv_undef;
877         TX_RETURN_NEXT();
878     }
879 
880     /* create a new frame */
881     cframe = tx_push_frame(aTHX_ TX_st);
882 
883     /* setup frame info: name, retaddr and output buffer */
884     sv_setsv(*av_fetch(cframe, TXframe_NAME,    TRUE), name);
885     sv_setuv(*av_fetch(cframe, TXframe_RETADDR, TRUE), PTR2UV(retaddr));
886 
887     /* swap TXframe_OUTPUT and TX_st->output.
888        I know it's ugly. Any ideas?
889     */
890     tmp                             = *av_fetch(cframe, TXframe_OUTPUT, TRUE);
891     AvARRAY(cframe)[TXframe_OUTPUT] = TX_st->output;
892     TX_st->output                   = tmp;
893     sv_setpvs(tmp, "");
894     SvGROW(tmp, TX_HINT_SIZE);
895 
896     i = 0;
897     if(outer > 0) { /* refers outer lexical variales */
898         /* copies lexical variables from the old frame to the new one */
899         AV* const oframe = TX_frame_at(TX_st, TX_st->current_frame-1);
900         for(NOOP; i < outer; i++) {
901             IV const real_ix = i + TXframe_START_LVAR;
902             /* XXX: macros can refer to unallocated lvars */
903             SV* sv;
904             SV* v = AvARRAY(oframe)[real_ix];
905             if (v != NULL && AvFILLp(oframe) >= real_ix) {
906                  sv = sv_mortalcopy(v);
907             } else {
908                  sv = &PL_sv_undef;
909             }
910             av_store(cframe, real_ix , sv);
911             SvREFCNT_inc_simple_void_NN(sv);
912         }
913     }
914     if(items > 0) { /* has arguments */
915         dORIGMARK;
916         MARK++;
917         for(NOOP; MARK <= SP; i++) {
918             sv_setsv(TX_lvar(i), *MARK);
919             MARK++;
920         }
921         SP = ORIGMARK;
922         PUTBACK;
923     }
924     TX_st->pad = AvARRAY(cframe) + TXframe_START_LVAR;
925     TX_RETURN_PC(addr);
926 }
927 
928 /* The virtual machine code interpreter */
929 /* NOTE: tx_execute() must be surrounded in ENTER and LEAVE */
930 static void
tx_execute(pTHX_ pMY_CXT_ tx_state_t * const base,SV * const output,HV * const hv)931 tx_execute(pTHX_ pMY_CXT_ tx_state_t* const base, SV* const output, HV* const hv) {
932     dXCPT;
933     tx_state_t st;
934 
935     StructCopy(base, &st, tx_state_t);
936 
937     //PerlIO_stdoutf("#>> 0x%p %d %d\n", base, (int)MY_CXT.depth, (int)st.current_frame);
938     st.output = output;
939     st.vars   = hv;
940 
941     assert(st.tmpl != NULL);
942 
943     /* local $current_st */
944     SAVEVPTR(MY_CXT.current_st);
945     MY_CXT.current_st = &st;
946 
947     assert(MY_CXT.depth >= 0);
948     if(MY_CXT.depth > TX_MAX_DEPTH) {
949         croak("Execution is too deep (> %d)", TX_MAX_DEPTH);
950     }
951 
952     /* local $depth = $depth + 1 */
953     MY_CXT.depth++;
954 
955     XCPT_TRY_START {
956         TX_RUNOPS(&st);
957     }
958     XCPT_TRY_END;
959 
960     /* finally */
961     MY_CXT.depth--;
962 
963     XCPT_CATCH {
964         I32 const start = base->current_frame;
965         /* unwind the stack frames */
966         while(st.current_frame > start) {
967             tx_pop_frame(aTHX_ &st, TRUE);
968         }
969         tx_pop_frame(aTHX_ base, FALSE); // pushed before tx_execute()
970         XCPT_RETHROW;
971     }
972     tx_pop_frame(aTHX_ base, FALSE); // pushed before tx_execute()
973 
974     /* clear temporary buffers */
975     sv_setsv(st.targ, NULL);
976 
977     /* store the current buffer size as a hint size */
978     base->hint_size = SvCUR(st.output);
979 }
980 
981 
982 static MAGIC*
mgx_find(pTHX_ SV * const sv,const MGVTBL * const vtbl)983 mgx_find(pTHX_ SV* const sv, const MGVTBL* const vtbl){
984     MAGIC* mg;
985 
986     assert(sv   != NULL);
987     assert(vtbl != NULL);
988 
989     for(mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic){
990         if(mg->mg_virtual == vtbl){
991             assert(mg->mg_type == PERL_MAGIC_ext);
992             return mg;
993         }
994     }
995 
996     return NULL;
997 }
998 
999 static int
tx_mg_free(pTHX_ SV * const sv,MAGIC * const mg)1000 tx_mg_free(pTHX_ SV* const sv, MAGIC* const mg){
1001     tx_state_t* const st      = (tx_state_t*)mg->mg_ptr;
1002     tx_info_t* const info     = st->info;
1003     tx_code_t* const code     = st->code;
1004     I32 const len             = st->code_len;
1005     I32 i;
1006 
1007     // PerlIO_stdoutf("# tx_mg_free()\n");
1008     for(i = 0; i < len; i++) {
1009         /* opcode */
1010         if( tx_oparg[ info[i].optype ] & TXARGf_SV ) {
1011             SvREFCNT_dec(code[i].u_arg.sv);
1012         }
1013 
1014         /* opinfo */
1015         SvREFCNT_dec(info[i].file);
1016     }
1017 
1018     Safefree(code);
1019     Safefree(info);
1020 
1021     SvREFCNT_dec(st->symbol);
1022     SvREFCNT_dec(st->frames);
1023     SvREFCNT_dec(st->targ);
1024     SvREFCNT_dec(st->engine);
1025 
1026     PERL_UNUSED_ARG(sv);
1027 
1028     return 0;
1029 }
1030 
1031 #ifdef USE_ITHREADS
1032 static SV*
tx_sv_dup_inc(pTHX_ SV * const sv,CLONE_PARAMS * const param)1033 tx_sv_dup_inc(pTHX_ SV* const sv, CLONE_PARAMS* const param) {
1034     return SvREFCNT_inc( sv_dup(sv, param) );
1035 }
1036 #endif
1037 
1038 static int
tx_mg_dup(pTHX_ MAGIC * const mg,CLONE_PARAMS * const param)1039 tx_mg_dup(pTHX_ MAGIC* const mg, CLONE_PARAMS* const param){
1040 #ifdef USE_ITHREADS /* single threaded perl has no "xxx_dup()" APIs */
1041     tx_state_t*       st        = (tx_state_t*)mg->mg_ptr;
1042     tx_info_t* const proto_info = st->info;
1043     tx_code_t* const proto_code = st->code;
1044     U32 const len               = st->code_len;
1045     U32 i;
1046 
1047     Newx(st->code, len, tx_code_t);
1048     Newx(st->info, len, tx_info_t);
1049 
1050     for(i = 0; i < len; i++) {
1051         U8 const oparg = tx_oparg[ proto_info[i].optype ];
1052         /* opcode */
1053         st->code[i].exec_code = proto_code[i].exec_code;
1054         if( oparg & TXARGf_SV ) {
1055             st->code[i].u_arg.sv = tx_sv_dup_inc(aTHX_ proto_code[i].u_arg.sv, param);
1056         }
1057         else if ( oparg & TXARGf_INT ) {
1058             st->code[i].u_arg.iv = proto_code[i].u_arg.iv;
1059         }
1060         else if( oparg & TXARGf_PC ) {
1061             st->code[i].u_arg.pc = proto_code[i].u_arg.pc;
1062         }
1063 
1064         /* opinfo */
1065         st->info[i].optype    = proto_info[i].optype;
1066         st->info[i].line      = proto_info[i].line;
1067         st->info[i].file      = tx_sv_dup_inc(aTHX_ proto_info[i].file, param);
1068     }
1069 
1070     st->symbol   = (HV*)tx_sv_dup_inc(aTHX_ (SV*)st->symbol, param);
1071     st->frames   = (AV*)tx_sv_dup_inc(aTHX_ (SV*)st->frames,   param);
1072     st->targ     =      tx_sv_dup_inc(aTHX_ st->targ, param);
1073     st->engine   =      tx_sv_dup_inc(aTHX_ st->engine, param);
1074 #else
1075     PERL_UNUSED_ARG(mg);
1076     PERL_UNUSED_ARG(param);
1077 #endif
1078     return 0;
1079 }
1080 
1081 
1082 static MGVTBL xslate_vtbl = { /* for identity */
1083     NULL, /* get */
1084     NULL, /* set */
1085     NULL, /* len */
1086     NULL, /* clear */
1087     tx_mg_free, /* free */
1088     NULL, /* copy */
1089     tx_mg_dup, /* dup */
1090 #ifdef MGf_LOCAL
1091     NULL,  /* local */
1092 #endif
1093 };
1094 
1095 
1096 static void
tx_invoke_load_file(pTHX_ SV * const self,SV * const name,SV * const mtime,bool const from_include)1097 tx_invoke_load_file(pTHX_ SV* const self, SV* const name, SV* const mtime, bool const from_include) {
1098     dSP;
1099     ENTER;
1100     SAVETMPS;
1101 
1102     PUSHMARK(SP);
1103     EXTEND(SP, 3);
1104     PUSHs(self);
1105     PUSHs(name);
1106     PUSHs(mtime ? mtime : &PL_sv_undef);
1107     PUSHs(boolSV(from_include));
1108     PUTBACK;
1109 
1110     call_method("load_file", G_EVAL | G_VOID | G_DISCARD);
1111     if(TX_CATCH_ERROR()){
1112         dMY_CXT;
1113         SV* const msg = PL_diehook == MY_CXT.die_handler
1114             ? sv_2mortal(newRV_inc(sv_mortalcopy(ERRSV)))
1115             : ERRSV;
1116         tx_call_error_handler(aTHX_ MY_CXT.die_handler, msg);
1117         /* not reached */
1118     }
1119 
1120     FREETMPS;
1121     LEAVE;
1122 }
1123 
1124 static bool
tx_all_deps_are_fresh(pTHX_ AV * const tmpl,Time_t const cache_mtime)1125 tx_all_deps_are_fresh(pTHX_ AV* const tmpl, Time_t const cache_mtime) {
1126     I32 const len = AvFILLp(tmpl) + 1;
1127     I32 i;
1128     Stat_t st;
1129 
1130     for(i = TXo_FULLPATH; i < len; i++) {
1131         SV* const deppath = AvARRAY(tmpl)[i];
1132 
1133         if(SvROK(deppath)) {
1134             continue;
1135         }
1136 
1137         //PerlIO_stdoutf("check deps: %"SVf" ...\n", deppath); // */
1138         if(PerlLIO_stat(SvPV_nolen_const(deppath), &st) < 0
1139                || st.st_mtime > cache_mtime) {
1140             SV* const main_cache = AvARRAY(tmpl)[TXo_CACHEPATH];
1141             /* compiled caches are no longer fresh, so it must be discarded */
1142 
1143             if(i != TXo_FULLPATH && SvOK(main_cache)) {
1144                 PerlLIO_unlink(SvPV_nolen_const(main_cache));
1145             }
1146             //PerlLIO_unlink(SvPV_nolen_const(AvARRAY(tmpl);
1147 
1148             if (dump_load) {
1149                 PerlIO_printf(PerlIO_stderr(),
1150                     "#[XS]   %"SVf": too old (%d < %d)\n",
1151                     deppath, (int)cache_mtime, (int)st.st_mtime);
1152             }
1153             return FALSE;
1154         }
1155         else {
1156             if (dump_load) {
1157                 PerlIO_printf(PerlIO_stderr(),
1158                     "#[XS]   %"SVf": fresh enough (%d >= %d)\n",
1159                     deppath, (int)cache_mtime, (int)st.st_mtime);
1160             }
1161         }
1162     }
1163     return TRUE;
1164 }
1165 
1166 static tx_state_t*
tx_load_template(pTHX_ SV * const self,SV * const name,bool const from_include)1167 tx_load_template(pTHX_ SV* const self, SV* const name, bool const from_include) {
1168     HV* hv;
1169     const char* why = NULL;
1170     HE* he;
1171     SV** svp;
1172     SV* sv;
1173     HV* ttable;
1174     AV* tmpl;
1175     MAGIC* mg;
1176     SV* cache_mtime;
1177     int retried = 0;
1178 
1179     if (dump_load) {
1180         PerlIO_printf(PerlIO_stderr(),
1181             "#[XS] load_template(%"SVf")\n", name);
1182     }
1183 
1184     if(!SvOK(name)) {
1185         why = "template name is invalid";
1186         goto err;
1187     }
1188 
1189     assert( SvROK(self) && SvTYPE(SvRV(self)) == SVt_PVHV );
1190 
1191     hv = (HV*)SvRV(self);
1192 
1193     retry:
1194     if(retried > 1) {
1195         why = "retried reloading, but failed";
1196         goto err;
1197     }
1198 
1199     /* validation by modified time (mtime) */
1200 
1201     /* my $ttable = $self->{template} */
1202     svp = hv_fetchs(hv, "template", FALSE);
1203     if(!svp) {
1204         why = "template table is not found";
1205         goto err;
1206     }
1207 
1208     sv = *svp;
1209     if(!(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV)) {
1210         why = "template table is not a HASH reference";
1211         goto err;
1212     }
1213 
1214     ttable = (HV*)SvRV(sv);
1215 
1216     /* $tmpl = $ttable->{$name} */
1217     he = hv_fetch_ent(ttable, name, FALSE, 0U);
1218     if(!he) {
1219         tx_invoke_load_file(aTHX_ self, name, NULL, from_include);
1220         retried++;
1221         goto retry;
1222     }
1223 
1224     sv = hv_iterval(ttable, he);
1225     if(!(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)) {
1226         why = "template entry is invalid";
1227         goto err;
1228     }
1229 
1230     tmpl = (AV*)SvRV(sv);
1231     if(AvFILLp(tmpl) < (TXo_least_size-1)) {
1232         why = form("template entry is broken (size: %d < %d)",
1233             (int)AvFILLp(tmpl)+1, (int)TXo_least_size);
1234         goto err;
1235     }
1236 
1237     mg  = mgx_find(aTHX_ (SV*)tmpl, &xslate_vtbl);
1238     if(!mg) {
1239         croak("Xslate: Invalid template holder was passed");
1240     }
1241     /* check mtime */
1242 
1243     cache_mtime = AvARRAY(tmpl)[TXo_MTIME];
1244 
1245     /* NOTE: Ensure the life of the template object  */
1246     sv_2mortal( (SV*)SvREFCNT_inc_simple_NN(tmpl) );
1247 
1248     if(!SvOK(cache_mtime)) { /* non-checking mode (i.e. release mode) */
1249 
1250         return (tx_state_t*)mg->mg_ptr;
1251     }
1252 
1253     if (dump_load) {
1254         PerlIO_printf(PerlIO_stderr(),
1255             "#[XS]   %"SVf" (mtime=%"SVf")\n", name, cache_mtime);
1256     }
1257 
1258     if(retried > 0 /* if already retried, it should be valid */
1259             || tx_all_deps_are_fresh(aTHX_ tmpl, SvIVX(cache_mtime))) {
1260         return (tx_state_t*)mg->mg_ptr;
1261     }
1262     else {
1263         tx_invoke_load_file(aTHX_ self, name, cache_mtime, from_include);
1264         retried++;
1265         goto retry;
1266     }
1267 
1268     err:
1269     croak("Xslate: Cannot load template %s: %s", tx_neat(aTHX_ name), why);
1270 }
1271 
1272 static int
tx_macro_free(pTHX_ SV * const sv PERL_UNUSED_DECL,MAGIC * const mg)1273 tx_macro_free(pTHX_ SV* const sv PERL_UNUSED_DECL, MAGIC* const mg){
1274     CV* const xsub = (CV*)mg->mg_obj;
1275 
1276     assert(SvTYPE(xsub) == SVt_PVCV);
1277     assert(CvXSUB(xsub) != NULL);
1278 
1279     CvXSUBANY(xsub).any_ptr = NULL;
1280     return 0;
1281 }
1282 
1283 static MGVTBL macro_vtbl = { /* identity */
1284     NULL, /* get */
1285     NULL, /* set */
1286     NULL, /* len */
1287     NULL, /* clear */
1288     tx_macro_free, /* free */
1289     NULL, /* copy */
1290     NULL, /* dup */
1291 #ifdef MGf_LOCAL
1292     NULL,  /* local */
1293 #endif
1294 };
1295 
1296 
1297 static void
tx_my_cxt_init(pTHX_ pMY_CXT_ bool const cloning PERL_UNUSED_DECL)1298 tx_my_cxt_init(pTHX_ pMY_CXT_ bool const cloning PERL_UNUSED_DECL) {
1299     MY_CXT.depth = 0;
1300     MY_CXT.raw_stash     = gv_stashpvs(TX_RAW_CLASS, GV_ADDMULTI);
1301     MY_CXT.macro_stash   = gv_stashpvs(TX_MACRO_CLASS, GV_ADDMULTI);
1302     MY_CXT.warn_handler  = SvREFCNT_inc_NN(
1303         (SV*)get_cv("Text::Xslate::Engine::_warn", GV_ADD));
1304     MY_CXT.die_handler   = SvREFCNT_inc_NN(
1305         (SV*)get_cv("Text::Xslate::Engine::_die",  GV_ADD));
1306     MY_CXT.make_error    = SvREFCNT_inc_NN(
1307         (SV*)get_cv("Text::Xslate::Engine::make_error",  GV_ADD));
1308 }
1309 
1310 /* Because overloading stuff of old xsubpp didn't work,
1311    we need to copy them. */
1312 XS(XS_Text__Xslate__fallback); /* prototype to pass -Wmissing-prototypes */
XS(XS_Text__Xslate__fallback)1313 XS(XS_Text__Xslate__fallback)
1314 {
1315    dXSARGS;
1316    PERL_UNUSED_VAR(cv);
1317    PERL_UNUSED_VAR(items);
1318    XSRETURN_EMPTY;
1319 }
1320 
1321 EXTERN_C XS(boot_Text__Xslate__Methods);
1322 
1323 MODULE = Text::Xslate    PACKAGE = Text::Xslate::Engine
1324 
1325 PROTOTYPES: DISABLE
1326 
1327 BOOT:
1328 {
1329     HV* const ops = get_hv("Text::Xslate::OPS", GV_ADDMULTI);
1330     MY_CXT_INIT;
1331     tx_my_cxt_init(aTHX_ aMY_CXT_ FALSE);
1332     tx_init_ops(aTHX_ ops);
1333 
1334     {
1335         PUSHMARK(SP);
1336         boot_Text__Xslate__Methods(aTHX_ cv);
1337     }
1338 }
1339 
1340 #ifdef USE_ITHREADS
1341 
1342 void
CLONE(...)1343 CLONE(...)
1344 CODE:
1345 {
1346     MY_CXT_CLONE;
1347     tx_my_cxt_init(aTHX_ aMY_CXT_ FALSE);
1348     PERL_UNUSED_VAR(items);
1349 }
1350 
1351 #endif
1352 
1353 void
_register_builtin_methods(self,HV * hv)1354 _register_builtin_methods(self, HV* hv)
1355 CODE:
1356 {
1357     tx_register_builtin_methods(aTHX_ hv);
1358 }
1359 
1360 void
_assemble(HV * self,AV * proto,SV * name,SV * fullpath,SV * cachepath,SV * mtime)1361 _assemble(HV* self, AV* proto, SV* name, SV* fullpath, SV* cachepath, SV* mtime)
1362 CODE:
1363 {
1364     dMY_CXT;
1365     dTX_optable;
1366     MAGIC* mg;
1367     HV* hv;
1368     HV* const ops = get_hv("Text::Xslate::OPS", GV_ADD);
1369     U32 const len = av_len(proto) + 1;
1370     U32 i;
1371     U16 oi_line; /* opinfo.line */
1372     SV* oi_file;
1373     tx_state_t st;
1374     AV* tmpl;
1375     SV* tobj;
1376     SV** svp;
1377     AV* macro = NULL;
1378 
1379     TAINT_NOT; /* All the SVs we'll create here are safe */
1380 
1381     Zero(&st, 1, tx_state_t);
1382 
1383     svp = hv_fetchs(self, "template", FALSE);
1384     if(!(svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV)) {
1385         croak("The xslate instance has no template table");
1386     }
1387     hv = (HV*)SvRV(*svp);
1388 
1389     if(!SvOK(name)) {
1390         croak("Undefined template name is invalid");
1391     }
1392 
1393     /* fetch the template object from $self->{template}{$name} */
1394     tobj = hv_iterval(hv, hv_fetch_ent(hv, name, TRUE, 0U));
1395 
1396     tmpl = newAV();
1397     /* store the template object to $self->{template}{$name} */
1398     sv_setsv(tobj, sv_2mortal(newRV_noinc((SV*)tmpl)));
1399     av_extend(tmpl, TXo_least_size - 1);
1400 
1401     sv_setsv(*av_fetch(tmpl, TXo_MTIME,     TRUE),  mtime);
1402     sv_setsv(*av_fetch(tmpl, TXo_CACHEPATH, TRUE),  cachepath);
1403     sv_setsv(*av_fetch(tmpl, TXo_FULLPATH,  TRUE),  fullpath);
1404 
1405     /* prepare function table */
1406     svp = hv_fetchs(self, "function", FALSE);
1407     TAINT_NOT;
1408 
1409     if(!( SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV )) {
1410         croak("Function table must be a HASH reference");
1411     }
1412     /* $self->{function} must be copied
1413        because it might be changed per templates */
1414     st.symbol = newHVhv( (HV*)SvRV(*svp) );
1415 
1416     st.tmpl   = tmpl;
1417     st.engine = newRV_inc((SV*)self);
1418     sv_rvweaken(st.engine);
1419 
1420     st.hint_size = TX_HINT_SIZE;
1421 
1422     st.sa       = &PL_sv_undef;
1423     st.sb       = &PL_sv_undef;
1424     st.targ     = newSV(0);
1425 
1426     /* stack frame */
1427     st.frames        = newAV();
1428     st.current_frame = -1;
1429 
1430     Newxz(st.info, len + 1, tx_info_t);
1431     st.info[len].line = (U16)-1; /* invalid value */
1432     st.info[len].file = SvREFCNT_inc_simple_NN(name);
1433 
1434     Newxz(st.code, len, tx_code_t);
1435 
1436     st.code_len = len;
1437     st.pc       = &st.code[0];
1438 
1439     mg = sv_magicext((SV*)tmpl, NULL, PERL_MAGIC_ext,
1440         &xslate_vtbl, (char*)&st, sizeof(st));
1441     mg->mg_flags |= MGf_DUP;
1442 
1443     oi_line = 0;
1444     oi_file = name;
1445 
1446     for(i = 0; i < len; i++) {
1447         SV* const code = *av_fetch(proto, i, TRUE);
1448         if(SvROK(code) && SvTYPE(SvRV(code)) == SVt_PVAV) {
1449             AV* const av     = (AV*)SvRV(code);
1450             SV* const opname = *av_fetch(av, 0, TRUE);
1451             SV** const arg   =  av_fetch(av, 1, FALSE);
1452             SV** const line  =  av_fetch(av, 2, FALSE);
1453             SV** const file  =  av_fetch(av, 3, FALSE);
1454             HE* const he     = hv_fetch_ent(ops, opname, FALSE, 0U);
1455             IV  opnum;
1456 
1457             if(!he){
1458                 croak("Oops: Unknown opcode '%"SVf"' on [%d]", opname, (int)i);
1459             }
1460 
1461             opnum                = SvIVx(hv_iterval(ops, he));
1462             st.code[i].exec_code = tx_optable[opnum];
1463             if(tx_oparg[opnum] & TXARGf_SV) {
1464                 if(!arg) {
1465                     croak("Oops: Opcode %"SVf" must have an argument on [%d]", opname, (int)i);
1466                 }
1467 
1468                 if(tx_oparg[opnum] & TXARGf_KEY) { /* shared sv */
1469                     STRLEN len;
1470                     const char* const pv = SvPV_const(*arg, len);
1471                     st.code[i].u_arg.sv = newSVpvn_share(pv, SvUTF8(*arg) ? -len : len, 0U);
1472                 }
1473                 else if(tx_oparg[opnum] & TXARGf_INT) { /* sviv */
1474                     SvIV_please(*arg);
1475                     st.code[i].u_arg.sv = SvIsUV(*arg)
1476                         ? newSVuv(SvUV(*arg))
1477                         : newSViv(SvIV(*arg));
1478                 }
1479                 else { /* normal sv */
1480                     st.code[i].u_arg.sv = newSVsv(*arg);
1481                 }
1482             }
1483             else if(tx_oparg[opnum] & TXARGf_INT) {
1484                 st.code[i].u_arg.iv = SvIV(*arg);
1485             }
1486             else if(tx_oparg[opnum] & TXARGf_PC) {
1487                 /* calculate relational addresses to absolute addresses */
1488                 UV const abs_pos       = (UV)(i + SvIV(*arg));
1489 
1490                 if(abs_pos >= (UV)len) {
1491                     croak("Oops: goto address %"IVdf" is out of range (must be 0 <= addr <= %"IVdf")",
1492                         SvIV(*arg), (IV)len);
1493                 }
1494                 st.code[i].u_arg.pc = TX_POS2PC(&st, abs_pos);
1495             }
1496             else {
1497                 if(arg && SvOK(*arg)) {
1498                     croak("Oops: Opcode %"SVf" has an extra argument %s on [%d]",
1499                         opname, tx_neat(aTHX_ *arg), (int)i);
1500                 }
1501             }
1502 
1503             /* setup opinfo */
1504             if(line && SvOK(*line)) {
1505                 oi_line = (U16)SvUV(*line);
1506             }
1507             if(file && SvOK(*file) && !sv_eq(*file, oi_file)) {
1508                 oi_file = sv_mortalcopy(*file);
1509             }
1510             st.info[i].optype = (U16)opnum;
1511             st.info[i].line   = oi_line;
1512             st.info[i].file   = SvREFCNT_inc_simple_NN(oi_file);
1513 
1514             /* special cases */
1515             if(opnum == TXOP_macro_begin) {
1516                 SV* const name = st.code[i].u_arg.sv;
1517                 SV* const ent  = hv_iterval(st.symbol,
1518                     hv_fetch_ent(st.symbol, name, TRUE, 0U));
1519 
1520                 if(!sv_true(ent)) {
1521                     SV* mref;
1522                     macro = newAV();
1523                     mref  = sv_2mortal(newRV_noinc((SV*)macro));
1524                     sv_bless(mref, MY_CXT.macro_stash);
1525                     sv_setsv(ent, mref);
1526 
1527                     (void)av_store(macro, TXm_OUTER, newSViv(0));
1528                     (void)av_store(macro, TXm_NARGS, newSViv(0));
1529                     (void)av_store(macro, TXm_ADDR,  newSVuv(PTR2UV(TX_POS2PC(&st, i))));
1530                     (void)av_store(macro, TXm_NAME,  name);
1531                     st.code[i].u_arg.sv = NULL;
1532                 }
1533                 else { /* already defined */
1534                     macro = NULL;
1535                 }
1536             }
1537             else if(opnum == TXOP_macro_nargs) {
1538                 if(macro) {
1539                     /* the number of outer lexical variables */
1540                     (void)av_store(macro, TXm_NARGS, st.code[i].u_arg.sv);
1541                     st.code[i].u_arg.sv = NULL;
1542                 }
1543             }
1544             else if(opnum == TXOP_macro_outer) {
1545                 if(macro) {
1546                     /* the number of outer lexical variables */
1547                     (void)av_store(macro, TXm_OUTER, st.code[i].u_arg.sv);
1548                     st.code[i].u_arg.sv = NULL;
1549                 }
1550             }
1551             else if(opnum == TXOP_depend) {
1552                 /* add a dependent file to the tmpl object */
1553                 av_push(tmpl, st.code[i].u_arg.sv);
1554                 st.code[i].u_arg.sv = NULL;
1555             }
1556         }
1557         else {
1558             croak("Oops: Broken code found on [%d]", (int)i);
1559         }
1560     } /* end for each code */
1561 }
1562 
1563 void
1564 render(SV* self, SV* source, SV* vars = &PL_sv_undef)
1565 ALIAS:
1566     render        = 0
1567     render_string = 1
1568 CODE:
1569 {
1570     dMY_CXT;
1571     tx_state_t* st;
1572 
1573     TAINT_NOT; /* All the SVs we'll create here are safe */
1574 
1575     /* $_[0]: engine */
1576     if(!(SvROK(self) && SvTYPE(SvRV(self)) == SVt_PVHV)) {
1577         croak("Xslate: Invalid xslate instance: %s",
1578             tx_neat(aTHX_ self));
1579     }
1580 
1581     /* $_[1]: template source */
1582     if(ix == 1) { /* render_string() */
1583         dXSTARG;
1584         PUSHMARK(SP);
1585         EXTEND(SP, 2);
1586         PUSHs(self);
1587         PUSHs(source);
1588         PUTBACK;
1589         call_method("load_string", G_VOID | G_DISCARD);
1590         SPAGAIN;
1591         source = TARG;
1592         sv_setpvs(source, "<string>");
1593     }
1594 
1595     SvGETMAGIC(source);
1596     if(!SvOK(source)) {
1597         croak("Xslate: Template name is not given");
1598     }
1599 
1600     /* $_[2]: template variable */
1601     if(!SvOK(vars)) {
1602         vars = sv_2mortal(newRV_noinc((SV*)newHV()));
1603     }
1604     else if(!(SvROK(vars) && SvTYPE(SvRV(vars)) == SVt_PVHV)) {
1605         croak("Xslate: Template variables must be a HASH reference, not %s",
1606             tx_neat(aTHX_ vars));
1607     }
1608     if(SvOBJECT(SvRV(vars))) {
1609         Perl_warner(aTHX_ packWARN(WARN_MISC),
1610             "Xslate: Template variables must be a HASH reference, not %s",
1611             tx_neat(aTHX_ vars));
1612     }
1613 
1614     st = tx_load_template(aTHX_ self, source, FALSE);
1615 
1616     /* local $SIG{__WARN__} = \&warn_handler */
1617     if (PL_warnhook != MY_CXT.warn_handler) {
1618         SAVEGENERICSV(PL_warnhook);
1619         MY_CXT.orig_warn_handler = PL_warnhook;
1620         PL_warnhook              = SvREFCNT_inc_NN(MY_CXT.warn_handler);
1621     }
1622 
1623     /* local $SIG{__DIE__}  = \&die_handler */
1624     if (PL_diehook != MY_CXT.die_handler) {
1625         SAVEGENERICSV(PL_diehook);
1626         MY_CXT.orig_die_handler = PL_diehook;
1627         PL_diehook              = SvREFCNT_inc_NN(MY_CXT.die_handler);
1628     }
1629 
1630     {
1631         AV* mainframe = tx_push_frame(aTHX_ st); // frame[0]
1632         SV* result = sv_newmortal();
1633         sv_grow(result, st->hint_size + TX_HINT_SIZE);
1634         SvPOK_on(result);
1635 
1636         av_store(mainframe, TXframe_NAME,    SvREFCNT_inc_simple_NN(source));
1637         av_store(mainframe, TXframe_RETADDR, newSVuv(st->code_len));
1638         tx_execute(aTHX_ aMY_CXT_ st, result, (HV*)SvRV(vars));
1639         ST(0) = result;
1640     }
1641 }
1642 
1643 void
validate(SV * self,SV * source)1644 validate(SV* self, SV* source)
1645 CODE:
1646 {
1647     TAINT_NOT; /* All the SVs we'll create here are safe */
1648 
1649     /* $_[0]: engine */
1650     if(!(SvROK(self) && SvTYPE(SvRV(self)) == SVt_PVHV)) {
1651         croak("Xslate: Invalid xslate instance: %s",
1652             tx_neat(aTHX_ self));
1653     }
1654 
1655     SvGETMAGIC(source);
1656     if(!SvOK(source)) {
1657         croak("Xslate: Template name is not given");
1658     }
1659 
1660     tx_load_template(aTHX_ self, source, FALSE);
1661 }
1662 
1663 void
current_engine(klass)1664 current_engine(klass)
1665 CODE:
1666 {
1667     dMY_CXT;
1668     tx_state_t* const st = MY_CXT.current_st;
1669     SV* retval;
1670     if(st) {
1671         if(ix == 0) { /* current_engine */
1672             retval = st->engine;
1673         }
1674         else if(ix == 1) { /* current_vars */
1675             retval = sv_2mortal(newRV_inc((SV*)st->vars));
1676         }
1677         else { /* current_file / current_line */
1678             const tx_info_t* const info
1679                 = &(st->info[ TX_PC2POS(st, st->pc) ]);
1680 
1681             retval = (ix == 2)
1682                 ? info->file
1683                 : sv_2mortal(newSViv(info->line));
1684         }
1685     }
1686     else {
1687         retval = &PL_sv_undef;
1688     }
1689     ST(0) = retval;
1690 }
1691 ALIAS:
1692     current_engine = 0
1693     current_vars   = 1
1694     current_file   = 2
1695     current_line   = 3
1696 
1697 void
print(klass,...)1698 print(klass, ...)
1699 CODE:
1700 {
1701     dMY_CXT;
1702     int i;
1703     tx_state_t* const st = MY_CXT.current_st;
1704     if(!st) {
1705         croak("You cannot call print() method outside render()");
1706     }
1707 
1708     for(i = 1; i < items; i++) {
1709         tx_print(aTHX_ st, ST(i));
1710     }
1711     XSRETURN_NO; /* return false as an empty string */
1712 }
1713 
1714 void
_warn(SV * msg)1715 _warn(SV* msg)
1716 ALIAS:
1717     _warn = 0
1718     _die  = 1
1719 CODE:
1720 {
1721     dMY_CXT;
1722     tx_state_t* const st = MY_CXT.current_st;
1723     SV* engine;
1724     AV* cframe;
1725     SV* name;
1726     SV* full_message;
1727     SV** svp;
1728     CV*  handler;
1729     UV pc_pos;
1730     SV* file;
1731 
1732 
1733     /* restore error handlers to avoid recursion */
1734     SAVESPTR(PL_warnhook);
1735     SAVESPTR(PL_diehook);
1736     PL_warnhook = MY_CXT.orig_warn_handler;
1737     PL_diehook  = MY_CXT.orig_die_handler;
1738     msg = sv_mortalcopy(msg);
1739 
1740     if(!st) {
1741         croak("%"SVf, msg);
1742     }
1743 
1744 
1745     engine = st->engine;
1746     cframe = TX_current_framex(st);
1747     name   = AvARRAY(cframe)[TXframe_NAME];
1748 
1749     svp = (ix == 0)
1750         ? hv_fetchs((HV*)SvRV(engine), "warn_handler", FALSE)
1751         : hv_fetchs((HV*)SvRV(engine), "die_handler",  FALSE);
1752 
1753     if(svp && SvOK(*svp)) {
1754         HV* stash;
1755         GV* gv;
1756         handler = sv_2cv(*svp, &stash, &gv, 0);
1757     }
1758     else {
1759         handler = NULL;
1760     }
1761 
1762     pc_pos = TX_PC2POS(st, st->pc);
1763     file   = st->info[ pc_pos ].file;
1764     if(strEQ(SvPV_nolen_const(file), "<string>")) {
1765         svp = hv_fetchs((HV*)SvRV(engine), "string_buffer", FALSE);
1766         if(svp) {
1767             file = sv_2mortal(newRV_inc(*svp));
1768         }
1769     }
1770     /* TODO: append the stack info to msg */
1771     /* $full_message = make_error(engine, msg, file, line, vm_pos) */
1772     PUSHMARK(SP);
1773     EXTEND(SP, 6);
1774     PUSHs(sv_mortalcopy(engine)); /* XXX: avoid premature free */
1775     PUSHs(msg);
1776     PUSHs(file);
1777     mPUSHi(st->info[ pc_pos ].line);
1778     if(tx_verbose(aTHX_ st) >= 3) {
1779         if(!SvOK(name)) { // FIXME: something's wrong
1780             name = newSVpvs_flags("(oops)", SVs_TEMP);
1781         }
1782         mPUSHs(newSVpvf("&%"SVf"[%"UVuf"]", name, pc_pos));
1783     }
1784     PUTBACK;
1785     call_sv(MY_CXT.make_error, G_SCALAR);
1786     SPAGAIN;
1787     full_message = POPs;
1788     PUTBACK;
1789 
1790     if(ix == 0) { /* warn */
1791         /* handler can ignore warnings */
1792         if(handler) {
1793             PUSHMARK(SP);
1794             XPUSHs(full_message);
1795             PUTBACK;
1796             call_sv((SV*)handler, G_VOID | G_DISCARD);
1797             /* handler can ignore errors */
1798         }
1799         else {
1800             warn("%"SVf, full_message);
1801         }
1802     }
1803     else {
1804         if(handler) {
1805             PUSHMARK(SP);
1806             XPUSHs(full_message);
1807             PUTBACK;
1808             call_sv((SV*)handler, G_VOID | G_DISCARD);
1809             /* handler cannot ignore errors */
1810         }
1811         croak("%"SVf, full_message); /* must die */
1812         /* not reached */
1813     }
1814 }
1815 
1816 MODULE = Text::Xslate    PACKAGE = Text::Xslate::Util
1817 
1818 void
mark_raw(SV * str)1819 mark_raw(SV* str)
1820 CODE:
1821 {
1822     ST(0) = tx_mark_raw(aTHX_ str);
1823 }
1824 
1825 void
unmark_raw(SV * str)1826 unmark_raw(SV* str)
1827 CODE:
1828 {
1829     ST(0) = tx_unmark_raw(aTHX_ str);
1830 }
1831 
1832 
1833 void
html_escape(SV * str)1834 html_escape(SV* str)
1835 CODE:
1836 {
1837     ST(0) = tx_html_escape(aTHX_ str);
1838 }
1839 
1840 void
uri_escape(SV * str)1841 uri_escape(SV* str)
1842 CODE:
1843 {
1844     ST(0) = tx_uri_escape(aTHX_ str);
1845 }
1846 
1847 void
is_array_ref(SV * sv)1848 is_array_ref(SV* sv)
1849 CODE:
1850 {
1851     ST(0) = boolSV( tx_sv_is_array_ref(aTHX_ sv));
1852 }
1853 
1854 void
is_hash_ref(SV * sv)1855 is_hash_ref(SV* sv)
1856 CODE:
1857 {
1858     ST(0) = boolSV( tx_sv_is_hash_ref(aTHX_ sv));
1859 }
1860 
1861 void
is_code_ref(SV * sv)1862 is_code_ref(SV* sv)
1863 CODE:
1864 {
1865     ST(0) = boolSV( tx_sv_is_code_ref(aTHX_ sv));
1866 }
1867 
1868 void
merge_hash(SV * base,SV * value)1869 merge_hash(SV* base, SV* value)
1870 CODE:
1871 {
1872     ST(0) = tx_merge_hash(aTHX_ NULL, base, value);
1873 }
1874 
1875 MODULE = Text::Xslate    PACKAGE = Text::Xslate::Type::Raw
1876 
1877 BOOT:
1878 {
1879     SV* as_string;
1880     /* overload stuff */
1881     PL_amagic_generation++;
1882     sv_setsv(
1883         get_sv( TX_RAW_CLASS "::()", TRUE ),
1884         &PL_sv_yes
1885     );
1886     (void)newXS( TX_RAW_CLASS "::()",
1887         XS_Text__Xslate__fallback, file);
1888 
1889     /* *{'(""'} = \&as_string */
1890     as_string = sv_2mortal(newRV_inc(
1891         (SV*)get_cv( TX_RAW_CLASS "::as_string", GV_ADD)));
1892     sv_setsv_mg(
1893         (SV*)gv_fetchpvs( TX_RAW_CLASS "::(\"\"", GV_ADDMULTI, SVt_PVCV),
1894         as_string);
1895 }
1896 
1897 void
new(SV * klass,SV * str)1898 new(SV* klass, SV* str)
1899 CODE:
1900 {
1901     if(SvROK(klass)) {
1902         croak("You cannot call %s->new() as an instance method", TX_RAW_CLASS);
1903     }
1904     if(strNE(SvPV_nolen_const(klass), TX_RAW_CLASS)) {
1905         croak("You cannot extend %s", TX_RAW_CLASS);
1906     }
1907     ST(0) = tx_mark_raw(aTHX_ tx_unmark_raw(aTHX_ str));
1908 }
1909 
1910 void
as_string(SV * self,...)1911 as_string(SV* self, ...)
1912 CODE:
1913 {
1914     if(!SvROK(self)) {
1915         croak("You cannot call %s->as_string() as a class method", TX_RAW_CLASS);
1916     }
1917     ST(0) = tx_unmark_raw(aTHX_ self);
1918 }
1919 
1920 MODULE = Text::Xslate    PACKAGE = Text::Xslate::Type::Macro
1921 
1922 
1923 BOOT:
1924 {
1925     SV* code_ref;
1926     /* overload stuff */
1927     PL_amagic_generation++;
1928     sv_setsv(
1929         get_sv( TX_MACRO_CLASS "::()", TRUE ),
1930         &PL_sv_yes
1931     );
1932     (void)newXS( TX_MACRO_CLASS "::()",
1933         XS_Text__Xslate__fallback, file);
1934 
1935     /* *{'(&{}'} = \&as_code_ref */
1936     code_ref = sv_2mortal(newRV_inc((SV*)get_cv( TX_MACRO_CLASS "::as_code_ref", GV_ADD)));
1937     sv_setsv_mg(
1938         (SV*)gv_fetchpvs( TX_MACRO_CLASS "::(&{}", GV_ADDMULTI, SVt_PVCV),
1939         code_ref);
1940 
1941     // debug flag
1942     code_ref = sv_2mortal(newRV_inc((SV*)get_cv( "Text::Xslate::Engine::_DUMP_LOAD", GV_ADD)));
1943     {
1944         dSP;
1945         PUSHMARK(SP);
1946         call_sv(code_ref, G_SCALAR);
1947         SPAGAIN;
1948         dump_load = sv_true(POPs);
1949         PUTBACK;
1950     }
1951 }
1952 
1953 CV*
as_code_ref(SV * self,...)1954 as_code_ref(SV* self, ...)
1955 CODE:
1956 {
1957     /* the macro object is responsible to its xsub's refcount */
1958     MAGIC* mg;
1959     CV* xsub;
1960 
1961     if(!tx_sv_is_macro(aTHX_ self)) {
1962         croak("Not a macro object: %s", tx_neat(aTHX_ self));
1963     }
1964 
1965     mg = mgx_find(aTHX_ SvRV(self), &macro_vtbl);
1966     if(!mg) {
1967         xsub = newXS(NULL, XS_Text__Xslate__macrocall, __FILE__);
1968         sv_magicext(SvRV(self), (SV*)xsub, PERL_MAGIC_ext, &macro_vtbl,
1969             NULL, 0);
1970         SvREFCNT_dec(xsub); /* refcnt++ in sv_magicext */
1971         CvXSUBANY(xsub).any_ptr = (void*)self;
1972     }
1973     else {
1974         xsub = (CV*)mg->mg_obj;
1975         assert(xsub);
1976         assert(SvTYPE(xsub) == SVt_PVCV);
1977     }
1978     RETVAL = xsub;
1979 }
1980 OUTPUT:
1981     RETVAL
1982