line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#ifdef __cplusplus |
2
|
|
|
|
|
|
|
extern "C" { |
3
|
|
|
|
|
|
|
#endif |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
#define PERL_NO_GET_CONTEXT /* we want efficiency */ |
6
|
|
|
|
|
|
|
#include |
7
|
|
|
|
|
|
|
#include |
8
|
|
|
|
|
|
|
#include |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
#ifdef __cplusplus |
11
|
|
|
|
|
|
|
} /* extern "C" */ |
12
|
|
|
|
|
|
|
#endif |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
#define NEED_newSVpvn_flags |
15
|
|
|
|
|
|
|
#include "ppport.h" |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
static XOP my_xop_tap; |
18
|
|
|
|
|
|
|
static XOP my_xop_push_sv; |
19
|
|
|
|
|
|
|
|
20
|
6
|
|
|
|
|
|
static OP *XS_B_Tap_pp_push_sv(pTHX) { |
21
|
12
|
|
|
|
|
|
dXSARGS; dORIGMARK; |
22
|
|
|
|
|
|
|
|
23
|
6
|
|
|
|
|
|
SV* const sv = cSVOP_sv; |
24
|
|
|
|
|
|
|
/* I know what this temporary variable is ugly. Patches welcome. */ |
25
|
6
|
|
|
|
|
|
SV * tmp = get_sv("B::Tap::_TMP", GV_ADD); |
26
|
6
|
|
|
|
|
|
sv_setsv(tmp, sv); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
/* Restore mark after work. */ |
29
|
6
|
50
|
|
|
|
|
PUSHMARK(ORIGMARK); |
30
|
|
|
|
|
|
|
|
31
|
6
|
|
|
|
|
|
RETURN; |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
|
34
|
6
|
|
|
|
|
|
static OP *XS_B_Tap_pp_tap(pTHX) { |
35
|
12
|
|
|
|
|
|
dXSARGS; dORIGMARK; |
36
|
|
|
|
|
|
|
int i; |
37
|
|
|
|
|
|
|
SV *tmp; |
38
|
6
|
|
|
|
|
|
AV *ret = newAV(); |
39
|
|
|
|
|
|
|
|
40
|
6
|
100
|
|
|
|
|
av_push(ret, newSViv(GIMME_V)); |
41
|
6
|
100
|
|
|
|
|
if (GIMME_V == G_SCALAR) { |
|
|
100
|
|
|
|
|
|
42
|
5
|
|
|
|
|
|
SvREFCNT_inc(ST(0)); |
43
|
5
|
|
|
|
|
|
av_push(ret, ST(0)); |
44
|
1
|
50
|
|
|
|
|
} else if (GIMME_V == G_VOID) { |
|
|
50
|
|
|
|
|
|
45
|
|
|
|
|
|
|
/* do nothing */ |
46
|
|
|
|
|
|
|
} else { |
47
|
1
|
|
|
|
|
|
AV * av = newAV(); |
48
|
5
|
100
|
|
|
|
|
for (i=0; i
|
49
|
4
|
|
|
|
|
|
SvREFCNT_inc(ST(i)); |
50
|
4
|
|
|
|
|
|
av_push(av, ST(i)); |
51
|
|
|
|
|
|
|
} |
52
|
1
|
|
|
|
|
|
av_push(ret, newRV_noinc((SV*)av)); |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
/* I know what this temporary variable is ugly. Patches welcome. */ |
56
|
6
|
|
|
|
|
|
tmp = get_sv("B::Tap::_TMP", GV_ADD); |
57
|
6
|
50
|
|
|
|
|
if (SvROK(tmp) && SvTYPE(SvRV(tmp)) == SVt_PVAV) { |
|
|
50
|
|
|
|
|
|
58
|
6
|
|
|
|
|
|
av_push((AV*)SvRV(tmp), newRV_noinc((SV*)ret)); |
59
|
|
|
|
|
|
|
} else { |
60
|
0
|
|
|
|
|
|
sv_dump(tmp); |
61
|
0
|
|
|
|
|
|
croak("ArrayRef is expected, but it's not ArrayRef."); |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
/* restore mark */ |
65
|
6
|
50
|
|
|
|
|
PUSHMARK(ORIGMARK); |
66
|
|
|
|
|
|
|
|
67
|
6
|
|
|
|
|
|
RETURN; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
/* characters, compatible with B::Concise */ |
71
|
|
|
|
|
|
|
static char tap_oa_char(int oa_class) { |
72
|
|
|
|
|
|
|
switch (oa_class) { |
73
|
|
|
|
|
|
|
/* |
74
|
|
|
|
|
|
|
case OA_OP: |
75
|
|
|
|
|
|
|
return '0'; */ |
76
|
|
|
|
|
|
|
case OA_UNOP: |
77
|
|
|
|
|
|
|
return '1'; |
78
|
|
|
|
|
|
|
case OA_BINOP: |
79
|
|
|
|
|
|
|
return '2'; |
80
|
|
|
|
|
|
|
case OA_LOGOP: |
81
|
|
|
|
|
|
|
return '|'; |
82
|
|
|
|
|
|
|
case OA_LISTOP: |
83
|
|
|
|
|
|
|
return '@'; |
84
|
|
|
|
|
|
|
case OA_PMOP: |
85
|
|
|
|
|
|
|
return '/'; |
86
|
|
|
|
|
|
|
case OA_SVOP: |
87
|
|
|
|
|
|
|
return '$'; |
88
|
|
|
|
|
|
|
/* |
89
|
|
|
|
|
|
|
case OA_PVOP: |
90
|
|
|
|
|
|
|
return '"'; */ |
91
|
|
|
|
|
|
|
case OA_LOOP: |
92
|
|
|
|
|
|
|
return '{'; |
93
|
|
|
|
|
|
|
case OA_COP: |
94
|
|
|
|
|
|
|
return ';'; |
95
|
|
|
|
|
|
|
case OA_PADOP: |
96
|
|
|
|
|
|
|
return '#'; |
97
|
|
|
|
|
|
|
default: |
98
|
|
|
|
|
|
|
return '-'; /* unknown */ |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
#define OP_CLASS_EX(op) \ |
103
|
|
|
|
|
|
|
((op)->op_type == OP_NULL ? (PL_opargs[(op)->op_targ] & OA_CLASS_MASK) : OP_CLASS((op))) |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
static char OA_CHAR(pTHX_ OP *op) { |
106
|
|
|
|
|
|
|
return tap_oa_char(OP_CLASS_EX(op)); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
#define TAP_TRACE(op, depth) \ |
110
|
|
|
|
|
|
|
{ \ |
111
|
|
|
|
|
|
|
int i; \ |
112
|
|
|
|
|
|
|
for (i=0;i
|
113
|
|
|
|
|
|
|
PerlIO_printf(PerlIO_stderr(), " "); \ |
114
|
|
|
|
|
|
|
} \ |
115
|
|
|
|
|
|
|
PerlIO_printf(PerlIO_stderr(), " rewriting: <%c", OA_CHAR(aTHX_ op)); \ |
116
|
|
|
|
|
|
|
PerlIO_printf(PerlIO_stderr(), "> "); \ |
117
|
|
|
|
|
|
|
if (op->op_type == OP_NULL) { \ |
118
|
|
|
|
|
|
|
PerlIO_printf(PerlIO_stderr(), "ex-%s", PL_op_name[op->op_targ]); \ |
119
|
|
|
|
|
|
|
} else { \ |
120
|
|
|
|
|
|
|
PerlIO_printf(PerlIO_stderr(), "%s", OP_NAME(op)); \ |
121
|
|
|
|
|
|
|
} \ |
122
|
|
|
|
|
|
|
PerlIO_printf(PerlIO_stderr(), "\n"); \ |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
#define RECURSE(next) rewrite_op(aTHX_ (OP*)next, orig, replacement, depth+1) |
127
|
|
|
|
|
|
|
#define REPLACE(type, meth) \ |
128
|
|
|
|
|
|
|
if (((type)target)->meth == orig) { \ |
129
|
|
|
|
|
|
|
((type)target)->meth = replacement; \ |
130
|
|
|
|
|
|
|
} else {\ |
131
|
|
|
|
|
|
|
RECURSE(((type)target)->meth); \ |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
120
|
|
|
|
|
|
static void rewrite_op(pTHX_ OP* target, OP* orig, OP* replacement, int depth) { |
135
|
|
|
|
|
|
|
/* TAP_TRACE(target, depth); */ |
136
|
|
|
|
|
|
|
|
137
|
104
|
100
|
|
|
|
|
switch (OP_CLASS_EX(target)) { |
|
|
100
|
|
|
|
|
|
138
|
|
|
|
|
|
|
case OA_UNOP: |
139
|
19
|
50
|
|
|
|
|
REPLACE(UNOP*, op_first); |
140
|
|
|
|
|
|
|
break; |
141
|
|
|
|
|
|
|
case OA_BINOP: |
142
|
19
|
100
|
|
|
|
|
REPLACE(BINOP*, op_first); |
143
|
|
|
|
|
|
|
break; |
144
|
|
|
|
|
|
|
case OA_LOGOP: |
145
|
0
|
0
|
|
|
|
|
REPLACE(LOGOP*, op_first); |
146
|
0
|
0
|
|
|
|
|
REPLACE(LOGOP*, op_other); |
147
|
|
|
|
|
|
|
break; |
148
|
|
|
|
|
|
|
case OA_LISTOP: |
149
|
18
|
50
|
|
|
|
|
REPLACE(LOGOP*, op_first); |
150
|
|
|
|
|
|
|
break; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
104
|
100
|
|
|
|
|
if (OpSIBLING(target)) { |
|
|
100
|
|
|
|
|
|
154
|
48
|
50
|
|
|
|
|
if (OpSIBLING(target) == orig) { |
|
|
100
|
|
|
|
|
|
155
|
4
|
|
|
|
|
|
OpMORESIB_set(target, replacement); |
156
|
|
|
|
|
|
|
} else { |
157
|
44
|
50
|
|
|
|
|
rewrite_op(aTHX_ (OP*)OpSIBLING(target), orig, replacement, depth); |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
} |
160
|
60
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
#undef RECURSE |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
MODULE = B::Tap PACKAGE = B::Tap |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
PROTOTYPES: DISABLE |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
BOOT: |
169
|
|
|
|
|
|
|
/* Register custom ops */ |
170
|
7
|
|
|
|
|
|
XopENTRY_set(&my_xop_tap, xop_name, "b_tap_tap"); |
171
|
7
|
|
|
|
|
|
XopENTRY_set(&my_xop_tap, xop_desc, "b_tap_tap"); |
172
|
7
|
|
|
|
|
|
XopENTRY_set(&my_xop_tap, xop_class, OA_BINOP); |
173
|
7
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ XS_B_Tap_pp_tap, &my_xop_tap); |
174
|
|
|
|
|
|
|
|
175
|
7
|
|
|
|
|
|
XopENTRY_set(&my_xop_push_sv, xop_name, "b_tap_push_sv"); |
176
|
7
|
|
|
|
|
|
XopENTRY_set(&my_xop_push_sv, xop_desc, "b_Tap_push_sv"); |
177
|
7
|
|
|
|
|
|
XopENTRY_set(&my_xop_push_sv, xop_class, OA_SVOP); |
178
|
7
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ XS_B_Tap_pp_push_sv, &my_xop_push_sv); |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
/* Register constats */ |
181
|
7
|
|
|
|
|
|
HV* stash = gv_stashpvn("B::Tap", strlen("B::Tap"), TRUE); |
182
|
7
|
|
|
|
|
|
newCONSTSUB(stash, "G_SCALAR", newSViv(G_SCALAR)); |
183
|
7
|
|
|
|
|
|
newCONSTSUB(stash, "G_ARRAY", newSViv(G_ARRAY)); |
184
|
7
|
|
|
|
|
|
newCONSTSUB(stash, "G_VOID", newSViv(G_VOID)); |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
void |
187
|
|
|
|
|
|
|
_tap(opp, root_opp, buf) |
188
|
|
|
|
|
|
|
void* opp; |
189
|
|
|
|
|
|
|
void* root_opp; |
190
|
|
|
|
|
|
|
SV * buf; |
191
|
|
|
|
|
|
|
CODE: |
192
|
|
|
|
|
|
|
{ |
193
|
|
|
|
|
|
|
/* Rewrite op tree. */ |
194
|
|
|
|
|
|
|
OP * orig_op = (OP*)opp; |
195
|
6
|
|
|
|
|
|
OP * next_op = orig_op->op_next; |
196
|
6
|
100
|
|
|
|
|
OP * sibling_op = OpSIBLING(orig_op); |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
/* |
199
|
|
|
|
|
|
|
* Before: |
200
|
|
|
|
|
|
|
* |
201
|
|
|
|
|
|
|
* (orig_op |
202
|
|
|
|
|
|
|
* next:next_op |
203
|
|
|
|
|
|
|
* sibling:sibling_op) |
204
|
|
|
|
|
|
|
* |
205
|
|
|
|
|
|
|
* After: |
206
|
|
|
|
|
|
|
* |
207
|
|
|
|
|
|
|
* (b_tap |
208
|
|
|
|
|
|
|
* first:(orig_op next:(push_sv next:b_tap)) |
209
|
|
|
|
|
|
|
* last:(b_tap_push_sv next:b_tap) |
210
|
|
|
|
|
|
|
* next:next_op |
211
|
|
|
|
|
|
|
* sibling:sibling_op |
212
|
|
|
|
|
|
|
* ) |
213
|
|
|
|
|
|
|
*/ |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
/* Create 'b_tap_push_sv' node */ |
216
|
6
|
|
|
|
|
|
SVOP * push_sv = (SVOP*)newSVOP(OP_CONST, 0, buf); |
217
|
6
|
|
|
|
|
|
push_sv->op_type = OP_CUSTOM; |
218
|
6
|
|
|
|
|
|
push_sv->op_ppaddr = XS_B_Tap_pp_push_sv; |
219
|
6
|
|
|
|
|
|
push_sv->op_flags = OPf_WANT_LIST; |
220
|
6
|
|
|
|
|
|
push_sv->op_sv = buf; |
221
|
|
|
|
|
|
|
SvREFCNT_inc(buf); |
222
|
|
|
|
|
|
|
|
223
|
6
|
|
|
|
|
|
BINOP * b_tap = (BINOP*)newBINOP(OP_NULL, 0, orig_op, (OP*)push_sv); |
224
|
6
|
|
|
|
|
|
b_tap->op_type = OP_CUSTOM; |
225
|
6
|
|
|
|
|
|
b_tap->op_ppaddr = XS_B_Tap_pp_tap; |
226
|
6
|
|
|
|
|
|
b_tap->op_flags = (orig_op->op_flags & OPf_WANT) | OPf_KIDS; |
227
|
6
|
|
|
|
|
|
b_tap->op_first = orig_op; |
228
|
6
|
|
|
|
|
|
b_tap->op_last = (OP*)push_sv; |
229
|
6
|
|
|
|
|
|
OpMORESIB_set(b_tap, sibling_op); |
230
|
|
|
|
|
|
|
|
231
|
6
|
|
|
|
|
|
orig_op->op_next = (OP*)push_sv; |
232
|
6
|
|
|
|
|
|
push_sv->op_next = (OP*)b_tap; |
233
|
6
|
|
|
|
|
|
b_tap->op_next = next_op; |
234
|
|
|
|
|
|
|
|
235
|
6
|
|
|
|
|
|
rewrite_op(aTHX_ (OP*)root_opp, (OP*)orig_op, (OP*)b_tap, 0); |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|