/********************* Xslate opcode definitions *********************/ /* vim: set filetype=xs: */ TXC(noop) { TX_RETURN_NEXT(); } TXC(move_to_sb) { TX_st_sb = TX_st_sa; TX_RETURN_NEXT(); } TXC(move_from_sb) { TX_st_sa = TX_st_sb; TX_RETURN_NEXT(); } TXC_w_var(save_to_lvar) { SV* const sv = TX_lvar(TX_op_arg_iv); sv_setsv(sv, TX_st_sa); TX_st_sa = sv; TX_RETURN_NEXT(); } TXC_w_var(load_lvar) { TX_st_sa = TX_lvar_get(TX_op_arg_iv); TX_RETURN_NEXT(); } TXC_w_var(load_lvar_to_sb) { TX_st_sb = TX_lvar_get(TX_op_arg_iv); TX_RETURN_NEXT(); } /* local $vars->{$key} = $val */ /* see pp_helem() in pp_hot.c */ TXC_w_key(localize_s) { HV* const vars = TX_st->vars; SV* const key = TX_op_arg_sv; bool const preeminent = hv_exists_ent(vars, key, 0U); HE* const he = hv_fetch_ent(vars, key, TRUE, 0U); SV* const newval = TX_st_sa; SV** const svp = &HeVAL(he); if(!preeminent) { STRLEN keylen; const char* const keypv = SvPV_const(key, keylen); SAVEDELETE(vars, savepvn(keypv, keylen), SvUTF8(key) ? -(I32)keylen : (I32)keylen); } else { save_helem(vars, key, svp); } sv_setsv_mg(*svp, newval); TX_RETURN_NEXT(); } /* local $engine->{vars} = expr; */ TXC(localize_vars) { SV* vars = sv_mortalcopy(TX_st_sa); if(!( SvROK(vars) && SvTYPE(SvRV(vars)) == SVt_PVHV && !SvOBJECT(SvRV(vars)) )) { tx_warn(aTHX_ TX_st, "Variable map must be a HASH reference, not %s", tx_neat(aTHX_ vars)); vars = sv_2mortal(newRV_noinc((SV*)newHV())); } save_hptr(&(TX_st->vars)); TX_st->vars = (HV*)SvRV(vars); TX_RETURN_NEXT(); } TXC(push) { dSP; XPUSHs(sv_mortalcopy(TX_st_sa)); PUTBACK; TX_RETURN_NEXT(); } TXC(pushmark) { dSP; PUSHMARK(SP); TX_RETURN_NEXT(); } TXC(nil) { TX_st_sa = &PL_sv_undef; TX_RETURN_NEXT(); } TXC_w_sv(literal) { TX_st_sa = TX_op_arg_sv; TX_RETURN_NEXT(); } /* the same as literal, but make sure its argument is an integer */ TXC_w_sviv(literal_i); TXC_w_key(fetch_s) { /* fetch a field from the top */ HV* const vars = TX_st->vars; HE* const he = hv_fetch_ent(vars, TX_op_arg_sv, FALSE, 0U); TX_st_sa = he ? hv_iterval(vars, he) : &PL_sv_undef; TX_RETURN_NEXT(); } TXC(fetch_field) { /* fetch a field from a variable (bin operator) */ SV* const var = TX_st_sb; SV* const key = TX_st_sa; TX_st_sa = tx_fetch(aTHX_ TX_st, var, key); TX_RETURN_NEXT(); } TXC_w_key(fetch_field_s) { /* fetch a field from a variable (for literal) */ SV* const var = TX_st_sa; SV* const key = TX_op_arg_sv; TX_st_sa = tx_fetch(aTHX_ TX_st, var, key); TX_RETURN_NEXT(); } TXC(print) { tx_print(aTHX_ TX_st, TX_st_sa); TX_RETURN_NEXT(); } TXC(print_raw) { SV* const arg = tx_unmark_raw(aTHX_ TX_st_sa); if(SvOK(arg)) { tx_sv_cat(aTHX_ TX_st->output, arg); } else { tx_warn(aTHX_ TX_st, "Use of nil to print"); } TX_RETURN_NEXT(); } TXC_w_sv(print_raw_s) { tx_sv_cat(aTHX_ TX_st->output, TX_op_arg_sv); TX_RETURN_NEXT(); } TXC(include) { dMY_CXT; tx_state_t* const st = tx_load_template(aTHX_ TX_st->engine, TX_st_sa, TRUE); ENTER; SAVETMPS; tx_push_frame(aTHX_ st); tx_execute(aTHX_ aMY_CXT_ st, TX_st->output, TX_st->vars); tx_pop_frame(aTHX_ st, FALSE); FREETMPS; LEAVE; TX_RETURN_NEXT(); } TXC_w_var(for_start) { SV* avref = TX_st_sa; IV const id = TX_op_arg_iv; SV* tmpsv; SvGETMAGIC(avref); if((tmpsv = tx_sv_to_ref(aTHX_ avref, SVt_PVAV, to_av_amg))) { avref = tmpsv; } else { if(SvOK(avref)) { tx_error(aTHX_ TX_st, "Iterating data must be an ARRAY reference, not %s", tx_neat(aTHX_ avref)); } avref = sv_2mortal(newRV_noinc((SV*)newAV())); } (void) TX_lvar(id+TXfor_ITEM); /* allocate the space */ sv_setiv(TX_lvar(id+TXfor_ITER), -1); /* (re)set iterator */ sv_setsv(TX_lvar(id+TXfor_ARRAY), avref); TX_RETURN_NEXT(); } TXC_goto(for_iter) { SV* const idsv = TX_st_sa; IV const id = SvIVX(idsv); /* by literal_i */ SV* const item = TX_lvar_get(id+TXfor_ITEM); SV* const i = TX_lvar_get(id+TXfor_ITER); SV* const avref = TX_lvar_get(id+TXfor_ARRAY); AV* const av = (AV*)SvRV(avref); assert(SvROK(avref)); assert(SvTYPE(av) == SVt_PVAV); assert(SvIOK(i)); SvIOK_only(i); /* for $^item */ SvIVX(i)++; //warn("for_next[%d %d]", (int)SvIV(i), (int)AvFILLp(av)); if(LIKELY(!SvRMAGICAL(av))) { if(SvIVX(i) <= AvFILLp(av)) { sv_setsv(item, AvARRAY(av)[SvIVX(i)]); TX_RETURN_NEXT(); } } else { /* magical variables */ if(SvIVX(i) <= av_len(av)) { SV** const itemp = av_fetch(av, SvIVX(i), FALSE); sv_setsv(item, itemp ? *itemp : NULL); TX_RETURN_NEXT(); } } /* the loop finished */ TX_st_sa = boolSV( SvIVX(i) > 0 ); /* for foreach-else */ tx_sv_clear(aTHX_ item); tx_sv_clear(aTHX_ i); tx_sv_clear(aTHX_ avref); TX_RETURN_PC( TX_op_arg_pc ); } /* sv_2iv(the guts of SvIV_please()) can make stringification faster, although I don't know why it is :) */ TXC(add) { sv_setnv(TX_st->targ, SvNVx(TX_st_sb) + SvNVx(TX_st_sa)); sv_2iv(TX_st->targ); /* IV please */ TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC(sub) { sv_setnv(TX_st->targ, SvNVx(TX_st_sb) - SvNVx(TX_st_sa)); sv_2iv(TX_st->targ); /* IV please */ TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC(mul) { sv_setnv(TX_st->targ, SvNVx(TX_st_sb) * SvNVx(TX_st_sa)); sv_2iv(TX_st->targ); /* IV please */ TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC(div) { sv_setnv(TX_st->targ, SvNVx(TX_st_sb) / SvNVx(TX_st_sa)); sv_2iv(TX_st->targ); /* IV please */ TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC(mod) { IV const lhs = SvIVx(TX_st_sb); IV const rhs = SvIVx(TX_st_sa); if(rhs == 0) { tx_error(aTHX_ TX_st, "Illegal modulus zero"); sv_setpvs(TX_st->targ, "NaN"); } else { sv_setiv(TX_st->targ, lhs % rhs); } TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC_w_sv(concat) { dMY_CXT; SV* sv = TX_op_arg_sv; SV* const lhs = TX_st_sb; SV* const rhs = TX_st_sa; if(tx_str_is_raw(aTHX_ aMY_CXT_ lhs)) { sv_setsv_nomg(sv, TX_UNMARK_RAW(lhs)); if(tx_str_is_raw(aTHX_ aMY_CXT_ rhs)) { sv_catsv_nomg(sv, TX_UNMARK_RAW(rhs)); } else { tx_sv_cat_with_html_escape_force(aTHX_ sv, rhs); } sv = tx_mark_raw(aTHX_ sv); } else { if(tx_str_is_raw(aTHX_ aMY_CXT_ rhs)) { sv_setpvs(sv, ""); tx_sv_cat_with_html_escape_force(aTHX_ sv, lhs); sv_catsv_nomg(sv, TX_UNMARK_RAW(rhs)); sv = tx_mark_raw(aTHX_ sv); } else { sv_setsv_nomg(sv, lhs); sv_catsv_nomg(sv, rhs); } } TX_st_sa = sv; TX_RETURN_NEXT(); } TXC_w_sv(repeat) { dMY_CXT; IV const lhs_is_raw = tx_str_is_raw(aTHX_ aMY_CXT_ TX_st_sb); SV* const lhs = lhs_is_raw ? TX_UNMARK_RAW(TX_st_sb) : TX_st_sb; SV* const rhs = TX_st_sa; SvGETMAGIC(lhs); if(!SvOK(lhs)) { tx_warn(aTHX_ TX_st, "Use of nil for repeat operator"); TX_st_sa = &PL_sv_undef; } else if(!looks_like_number(rhs)) { tx_error(aTHX_ TX_st, "Repeat count must be a number, not %s", tx_neat(aTHX_ TX_st_sa)); TX_st_sa = &PL_sv_undef; } else { STRLEN const len = sv_len(lhs); UV const count = SvUV(rhs); SV* const sv = TX_op_arg_sv; UV i; sv_setpvs(sv, ""); SvGROW(sv, len * count + 1); for(i = 0; i < count; i++) { tx_sv_cat(aTHX_ sv, lhs); } TX_st_sa = lhs_is_raw ? tx_mark_raw(aTHX_ sv) : sv; } TX_RETURN_NEXT(); } TXC(bitor) { sv_setuv(TX_st->targ, SvUVx(TX_st_sb) | SvUVx(TX_st_sa)); TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC(bitand) { sv_setuv(TX_st->targ, SvUVx(TX_st_sb) & SvUVx(TX_st_sa)); TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC(bitxor) { sv_setuv(TX_st->targ, SvUVx(TX_st_sb) ^ SvUVx(TX_st_sa)); TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC(bitneg) { sv_setuv(TX_st->targ, ~SvUVx(TX_st_sa)); TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC_goto(and) { if(sv_true(TX_st_sa)) { TX_RETURN_NEXT(); } else { TX_RETURN_PC( TX_op_arg_pc ); } } TXC_goto(dand) { SV* const sv = TX_st_sa; SvGETMAGIC(sv); if(SvOK(sv)) { TX_RETURN_NEXT(); } else { TX_RETURN_PC( TX_op_arg_pc ); } } TXC_goto(or) { if(!sv_true(TX_st_sa)) { TX_RETURN_NEXT(); } else { TX_RETURN_PC( TX_op_arg_pc ); } } TXC_goto(dor) { SV* const sv = TX_st_sa; SvGETMAGIC(sv); if(!SvOK(sv)) { TX_RETURN_NEXT(); } else { TX_RETURN_PC( TX_op_arg_pc ); } } TXC(not) { TX_st_sa = boolSV( !sv_true(TX_st_sa) ); TX_RETURN_NEXT(); } TXC(minus) { /* unary minus */ sv_setnv(TX_st->targ, -SvNVx(TX_st_sa)); sv_2iv(TX_st->targ); /* IV please */ TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC(max_index) { SV* const avref = TX_st_sa; if(!(SvROK(avref) && SvTYPE(SvRV(avref)) == SVt_PVAV)) { croak("Oops: Not an ARRAY reference for builtin max_index: %s", tx_neat(aTHX_ avref)); } sv_setiv(TX_st->targ, av_len((AV*)SvRV(avref))); TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC(builtin_mark_raw) { TX_st_sa = tx_mark_raw(aTHX_ TX_st_sa); TX_RETURN_NEXT(); } TXC(builtin_unmark_raw) { TX_st_sa = tx_unmark_raw(aTHX_ TX_st_sa); TX_RETURN_NEXT(); } TXC(builtin_uri_escape) { TX_st_sa = tx_uri_escape(aTHX_ TX_st_sa); TX_RETURN_NEXT(); } TXC(builtin_is_array_ref) { SV* const sv = TX_st_sa; SvGETMAGIC(sv); TX_st_sa = boolSV( tx_sv_is_array_ref(aTHX_ sv) ); TX_RETURN_NEXT(); } TXC(builtin_is_hash_ref) { SV* const sv = TX_st_sa; SvGETMAGIC(sv); TX_st_sa = boolSV( tx_sv_is_hash_ref(aTHX_ sv) ); TX_RETURN_NEXT(); } TXC(builtin_html_escape) { TX_st_sa = tx_html_escape(aTHX_ TX_st_sa); TX_RETURN_NEXT(); } TXC(match) { TX_st_sa = boolSV( tx_sv_match(aTHX_ TX_st_sb, TX_st_sa) ); TX_RETURN_NEXT(); } TXC(eq) { TX_st_sa = boolSV( tx_sv_eq(aTHX_ TX_st_sb, TX_st_sa) ); TX_RETURN_NEXT(); } TXC(ne) { TX_st_sa = boolSV( !tx_sv_eq(aTHX_ TX_st_sb, TX_st_sa) ); TX_RETURN_NEXT(); } TXC(lt) { TX_st_sa = boolSV( SvNVx(TX_st_sb) < SvNVx(TX_st_sa) ); TX_RETURN_NEXT(); } TXC(le) { TX_st_sa = boolSV( SvNVx(TX_ckuuv_lhs(TX_st_sb)) <= SvNVx(TX_ckuuv_rhs(TX_st_sa)) ); TX_RETURN_NEXT(); } TXC(gt) { TX_st_sa = boolSV( SvNVx(TX_ckuuv_lhs(TX_st_sb)) > SvNVx(TX_ckuuv_rhs(TX_st_sa)) ); TX_RETURN_NEXT(); } TXC(ge) { TX_st_sa = boolSV( SvNVx(TX_ckuuv_lhs(TX_st_sb)) >= SvNVx(TX_ckuuv_rhs(TX_st_sa)) ); TX_RETURN_NEXT(); } TXC(ncmp) { NV const lhs = SvNVx(TX_ckuuv_lhs(TX_st_sb)); NV const rhs = SvNVx(TX_ckuuv_rhs(TX_st_sa)); IV value; if(lhs == rhs) { value = 0; } else if(lhs < rhs) { value = -1; } else if(lhs > rhs) { value = 1; } else { /* compares NaN with something */ TX_st_sa = &PL_sv_undef; TX_RETURN_NEXT(); } sv_setiv(TX_st->targ, value); TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC(scmp) { sv_setiv(TX_st->targ, sv_cmp(TX_ckuuv_lhs(TX_st_sb), TX_ckuuv_rhs(TX_st_sa)) ); TX_st_sa = TX_st->targ; TX_RETURN_NEXT(); } TXC(range) { dSP; SV* const lhs = TX_st_sb; SV* const rhs = TX_st_sa; OP myop; Zero(&myop, 1, OP); myop.op_ppaddr = PL_ppaddr[OP_FLOP]; myop.op_type = OP_FLOP; /* call pp_flop() */ ENTER; SAVEOP(); PL_op = &myop; /* set GIMME to G_ARRAY (see op.h) */ PL_op->op_flags |= OPf_WANT_LIST; EXTEND(SP, 2); PUSHs(TX_ckuuv_lhs(lhs)); PUSHs(TX_ckuuv_rhs(rhs)); PUTBACK; myop.op_ppaddr(aTHX); LEAVE; TX_RETURN_NEXT(); } TXC_w_key(fetch_symbol) { /* functions, macros, constants */ SV* const name = TX_op_arg_sv; HE* he; if((he = hv_fetch_ent(TX_st->symbol, name, FALSE, 0U))) { TX_st_sa = HeVAL(he); } else { croak("Undefined symbol %s", tx_neat(aTHX_ name)); } TX_RETURN_NEXT(); } TXC(funcall) { /* call a function or a macro */ /* PUSHMARK must be done */ SV* const proc = TX_st_sa; if(tx_sv_is_macro(aTHX_ proc)) { tx_macro_enter(aTHX_ TX_st, (AV*)SvRV(proc), TX_st->pc + 1 /* retaddr */); } else { TX_st_sa = tx_funcall(aTHX_ TX_st, proc, "function call"); TX_RETURN_NEXT(); } } TXC(macro_end) { SV* const retaddr = AvARRAY(TX_current_frame())[TXframe_RETADDR]; TX_st_sa = tx_mark_raw(aTHX_ TX_st->output); tx_pop_frame(aTHX_ TX_st, TRUE); TX_RETURN_PC( INT2PTR(tx_pc_t, SvUVX(retaddr)) ); } TXC_w_key(methodcall_s) { TX_st_sa = tx_methodcall(aTHX_ TX_st, TX_op_arg_sv); TX_RETURN_NEXT(); } TXC(make_array) { /* PUSHMARK must be done */ dSP; dMARK; dORIGMARK; I32 const items = SP - MARK; AV* const av = newAV(); SV* const avref = sv_2mortal(newRV_noinc((SV*)av)); av_extend(av, items - 1); while(++MARK <= SP) { SV* const val = *MARK; /* the SV is a mortal copy */ /* see 'push' */ av_push(av, val); SvREFCNT_inc_simple_void_NN(val); } SP = ORIGMARK; PUTBACK; TX_st_sa = avref; TX_RETURN_NEXT(); } TXC(make_hash) { /* PUSHMARK must be done */ dSP; dMARK; dORIGMARK; I32 const items = SP - MARK; HV* const hv = newHV(); SV* const hvref = sv_2mortal(newRV_noinc((SV*)hv)); if((items % 2) != 0) { tx_error(aTHX_ TX_st, "Odd number of elements for hash literals"); XPUSHs(sv_newmortal()); } while(MARK < SP) { SV* const key = *(++MARK); SV* const val = *(++MARK); /* the SVs are a mortal copy */ /* see 'push' */ (void)hv_store_ent(hv, key, val, 0U); SvREFCNT_inc_simple_void_NN(val); } SP = ORIGMARK; PUTBACK; TX_st_sa = hvref; TX_RETURN_NEXT(); } TXC(enter) { ENTER; SAVETMPS; TX_RETURN_NEXT(); } TXC(leave) { FREETMPS; LEAVE; TX_RETURN_NEXT(); } TXC_goto(goto) { /* To catch SIGALRM and SIGXCPU */ PERL_ASYNC_CHECK(); TX_RETURN_PC( TX_op_arg_pc ); } TXC(vars) { TX_st_sa = sv_2mortal(newRV_inc((SV*)TX_st->vars)); TX_RETURN_NEXT(); } /* opcode markers (noop) */ TXC_w_sv(depend); /* tell the vm to dependent template files */ TXC_w_key(macro_begin); TXC_w_sviv(macro_nargs); TXC_w_sviv(macro_outer); TXC(set_opinfo); TXC(super); /* "end" must be here (the last opcode) */ TXC(end) { //assert(TX_st->current_frame == 0); }