Commit | Line | Data |
---|---|---|
ccc418af GS |
1 | #!./perl |
2 | ||
3 | BEGIN { | |
74517a3a | 4 | unshift @INC, 't'; |
9cd8f857 NC |
5 | require Config; |
6 | if (($Config::Config{'extensions'} !~ /\bB\b/) ){ | |
7 | print "1..0 # Skip -- Perl configured without B module\n"; | |
8 | exit 0; | |
9 | } | |
ccc418af GS |
10 | } |
11 | ||
12 | $| = 1; | |
13 | use warnings; | |
14 | use strict; | |
200b5b4b DD |
15 | BEGIN { |
16 | eval { require threads; threads->import; } | |
17 | } | |
f9a20969 | 18 | use Test::More; |
ccc418af | 19 | |
c5f0f3aa | 20 | BEGIN { use_ok( 'B' ); } |
ccc418af | 21 | |
08c6f5ec | 22 | |
87a42246 MS |
23 | package Testing::Symtable; |
24 | use vars qw($This @That %wibble $moo %moo); | |
25 | my $not_a_sym = 'moo'; | |
ccc418af | 26 | |
87a42246 MS |
27 | sub moo { 42 } |
28 | sub car { 23 } | |
ccc418af | 29 | |
f70490b9 | 30 | |
87a42246 MS |
31 | package Testing::Symtable::Foo; |
32 | sub yarrow { "Hock" } | |
f70490b9 | 33 | |
87a42246 MS |
34 | package Testing::Symtable::Bar; |
35 | sub hock { "yarrow" } | |
9b86dfa2 | 36 | |
87a42246 MS |
37 | package main; |
38 | use vars qw(%Subs); | |
39 | local %Subs = (); | |
40 | B::walksymtable(\%Testing::Symtable::, 'find_syms', sub { $_[0] =~ /Foo/ }, | |
41 | 'Testing::Symtable::'); | |
ccc418af | 42 | |
87a42246 MS |
43 | sub B::GV::find_syms { |
44 | my($symbol) = @_; | |
de3f1649 | 45 | |
87a42246 | 46 | $main::Subs{$symbol->STASH->NAME . '::' . $symbol->NAME}++; |
cfe9256d | 47 | } |
ccc418af | 48 | |
87a42246 MS |
49 | my @syms = map { 'Testing::Symtable::'.$_ } qw(This That wibble moo car |
50 | BEGIN); | |
51 | push @syms, "Testing::Symtable::Foo::yarrow"; | |
ccc418af | 52 | |
87a42246 | 53 | # Make sure we hit all the expected symbols. |
c5f0f3aa | 54 | ok( join('', sort @syms) eq join('', sort keys %Subs), 'all symbols found' ); |
1e1dbab6 | 55 | |
87a42246 | 56 | # Make sure we only hit them each once. |
c5f0f3aa RGS |
57 | ok( (!grep $_ != 1, values %Subs), '...and found once' ); |
58 | ||
59 | # Tests for MAGIC / MOREMAGIC | |
60 | ok( B::svref_2object(\$.)->MAGIC->TYPE eq "\0", '$. has \0 magic' ); | |
61 | { | |
62 | my $e = ''; | |
63 | local $SIG{__DIE__} = sub { $e = $_[0] }; | |
64 | # Used to dump core, bug #16828 | |
65 | eval { B::svref_2object(\$.)->MAGIC->MOREMAGIC->TYPE; }; | |
66 | like( $e, qr/Can't call method "TYPE" on an undefined value/, | |
67 | '$. has no more magic' ); | |
68 | } | |
01b509b0 | 69 | |
de64752d NC |
70 | { |
71 | my $pie = 'Good'; | |
72 | # This needs to be a package variable, as vars in the pad have some flags. | |
73 | my $r = B::svref_2object(\$::data2); | |
74 | is($r->FLAGS(), 0, "uninitialised package variable has flags of 0"); | |
75 | is($r->SvTYPE(), 0, "uninitialised package variable has type 0"); | |
76 | is($r->POK(), 0, "POK false"); | |
77 | is($r->ROK(), 0, "ROK false"); | |
78 | is($r->MAGICAL(), 0, "MAGICAL false"); | |
79 | $::data2 = $pie; | |
80 | isnt($r->FLAGS(), 0, "initialised package variable has nonzero flags"); | |
81 | isnt($r->SvTYPE(), 0, "initialised package variable has nonzero type"); | |
82 | isnt($r->POK(), 0, "POK true"); | |
83 | is($r->ROK(), 0, "ROK false"); | |
84 | is($r->MAGICAL(), 0, "MAGICAL false"); | |
85 | ||
86 | $::data2 = substr $pie, 0, 1; | |
87 | isnt($r->FLAGS(), 0, "initialised package variable has nonzero flags"); | |
88 | isnt($r->SvTYPE(), 0, "initialised package variable has nonzero type"); | |
89 | isnt($r->POK(), 0, "POK true"); | |
90 | is($r->ROK(), 0, "ROK false"); | |
91 | is($r->MAGICAL(), 0, "MAGICAL true"); | |
92 | ||
93 | $::data2 = \$pie; | |
94 | isnt($r->FLAGS(), 0, "initialised package variable has nonzero flags"); | |
95 | isnt($r->SvTYPE(), 0, "initialised package variable has nonzero type"); | |
96 | is($r->POK(), 0, "POK false"); | |
97 | isnt($r->ROK(), 0, "ROK true"); | |
98 | is($r->MAGICAL(), 0, "MAGICAL false"); | |
99 | ||
100 | is($r->REFCNT(), 1, "Reference count is 1"); | |
101 | { | |
102 | my $ref = \$::data2; | |
103 | is($r->REFCNT(), 2, "Second reference"); | |
104 | } | |
105 | is($r->REFCNT(), 1, "Reference count is 1"); | |
106 | ||
107 | } | |
108 | ||
5c35adbb NC |
109 | my $r = qr/foo/; |
110 | my $obj = B::svref_2object($r); | |
111 | my $regexp = ($] < 5.011) ? $obj->MAGIC : $obj; | |
112 | ok($regexp->precomp() eq 'foo', 'Get string from qr//'); | |
113 | like($regexp->REGEX(), qr/\d+/, "REGEX() returns numeric value"); | |
1f306347 | 114 | like($regexp->compflags, qr/^\d+\z/, "compflags returns numeric value"); |
4a31a506 FC |
115 | is B::svref_2object(qr/(?{time})/)->qr_anoncv->ROOT->first->name, 'qr', |
116 | 'qr_anoncv'; | |
01b509b0 SP |
117 | my $iv = 1; |
118 | my $iv_ref = B::svref_2object(\$iv); | |
119 | is(ref $iv_ref, "B::IV", "Test B:IV return from svref_2object"); | |
120 | is($iv_ref->REFCNT, 1, "Test B::IV->REFCNT"); | |
121 | # Flag tests are needed still | |
122 | #diag $iv_ref->FLAGS(); | |
123 | my $iv_ret = $iv_ref->object_2svref(); | |
124 | is(ref $iv_ret, "SCALAR", "Test object_2svref() return is SCALAR"); | |
125 | is($$iv_ret, $iv, "Test object_2svref()"); | |
126 | is($iv_ref->int_value, $iv, "Test int_value()"); | |
127 | is($iv_ref->IV, $iv, "Test IV()"); | |
128 | is($iv_ref->IVX(), $iv, "Test IVX()"); | |
129 | is($iv_ref->UVX(), $iv, "Test UVX()"); | |
f046b1bd NC |
130 | is(eval { $iv_ref->RV() }, undef, 'Test RV() on IV'); |
131 | like($@, qr/argument is not SvROK/, 'Test RV() IV'); | |
132 | $iv = \"Pie"; | |
133 | my $val = eval { $iv_ref->RV() }; | |
134 | is(ref $val, 'B::PV', 'Test RV() on a reference'); | |
135 | is($val->PV(), 'Pie', 'Value expected'); | |
136 | is($@, '', "Test RV()"); | |
01b509b0 SP |
137 | |
138 | my $pv = "Foo"; | |
139 | my $pv_ref = B::svref_2object(\$pv); | |
140 | is(ref $pv_ref, "B::PV", "Test B::PV return from svref_2object"); | |
141 | is($pv_ref->REFCNT, 1, "Test B::PV->REFCNT"); | |
142 | # Flag tests are needed still | |
143 | #diag $pv_ref->FLAGS(); | |
144 | my $pv_ret = $pv_ref->object_2svref(); | |
145 | is(ref $pv_ret, "SCALAR", "Test object_2svref() return is SCALAR"); | |
146 | is($$pv_ret, $pv, "Test object_2svref()"); | |
147 | is($pv_ref->PV(), $pv, "Test PV()"); | |
f046b1bd NC |
148 | is(eval { $pv_ref->RV() }, undef, 'Test RV() on PV'); |
149 | like($@, qr/argument is not SvROK/, 'Test RV() on PV'); | |
01b509b0 | 150 | is($pv_ref->PVX(), $pv, "Test PVX()"); |
f046b1bd NC |
151 | $pv = \"Pie"; |
152 | $val = eval { $pv_ref->RV() }; | |
153 | is(ref $val, 'B::PV', 'Test RV() on a reference'); | |
154 | is($val->PV(), 'Pie', 'Value expected'); | |
155 | is($@, '', "Test RV()"); | |
01b509b0 SP |
156 | |
157 | my $nv = 1.1; | |
158 | my $nv_ref = B::svref_2object(\$nv); | |
159 | is(ref $nv_ref, "B::NV", "Test B::NV return from svref_2object"); | |
160 | is($nv_ref->REFCNT, 1, "Test B::NV->REFCNT"); | |
161 | # Flag tests are needed still | |
162 | #diag $nv_ref->FLAGS(); | |
163 | my $nv_ret = $nv_ref->object_2svref(); | |
164 | is(ref $nv_ret, "SCALAR", "Test object_2svref() return is SCALAR"); | |
165 | is($$nv_ret, $nv, "Test object_2svref()"); | |
166 | is($nv_ref->NV, $nv, "Test NV()"); | |
167 | is($nv_ref->NVX(), $nv, "Test NVX()"); | |
f046b1bd NC |
168 | is(eval { $nv_ref->RV() }, undef, 'Test RV() on NV'); |
169 | like($@, qr/Can't locate object method "RV" via package "B::NV"/, | |
170 | 'Test RV() on NV'); | |
01b509b0 SP |
171 | |
172 | my $null = undef; | |
173 | my $null_ref = B::svref_2object(\$null); | |
174 | is(ref $null_ref, "B::NULL", "Test B::NULL return from svref_2object"); | |
175 | is($null_ref->REFCNT, 1, "Test B::NULL->REFCNT"); | |
176 | # Flag tests are needed still | |
177 | #diag $null_ref->FLAGS(); | |
178 | my $null_ret = $nv_ref->object_2svref(); | |
179 | is(ref $null_ret, "SCALAR", "Test object_2svref() return is SCALAR"); | |
180 | is($$null_ret, $nv, "Test object_2svref()"); | |
181 | ||
4df7f6af | 182 | my $RV_class = $] >= 5.011 ? 'B::IV' : 'B::RV'; |
01b509b0 SP |
183 | my $cv = sub{ 1; }; |
184 | my $cv_ref = B::svref_2object(\$cv); | |
4df7f6af NC |
185 | is($cv_ref->REFCNT, 1, "Test $RV_class->REFCNT"); |
186 | is(ref $cv_ref, "$RV_class", | |
187 | "Test $RV_class return from svref_2object - code"); | |
01b509b0 SP |
188 | my $cv_ret = $cv_ref->object_2svref(); |
189 | is(ref $cv_ret, "REF", "Test object_2svref() return is REF"); | |
190 | is($$cv_ret, $cv, "Test object_2svref()"); | |
191 | ||
192 | my $av = []; | |
193 | my $av_ref = B::svref_2object(\$av); | |
4df7f6af NC |
194 | is(ref $av_ref, "$RV_class", |
195 | "Test $RV_class return from svref_2object - array"); | |
01b509b0 SP |
196 | |
197 | my $hv = []; | |
198 | my $hv_ref = B::svref_2object(\$hv); | |
4df7f6af NC |
199 | is(ref $hv_ref, "$RV_class", |
200 | "Test $RV_class return from svref_2object - hash"); | |
01b509b0 SP |
201 | |
202 | local *gv = *STDOUT; | |
203 | my $gv_ref = B::svref_2object(\*gv); | |
204 | is(ref $gv_ref, "B::GV", "Test B::GV return from svref_2object"); | |
205 | ok(! $gv_ref->is_empty(), "Test is_empty()"); | |
711fbbf0 | 206 | ok($gv_ref->isGV_with_GP(), "Test isGV_with_GP()"); |
01b509b0 SP |
207 | is($gv_ref->NAME(), "gv", "Test NAME()"); |
208 | is($gv_ref->SAFENAME(), "gv", "Test SAFENAME()"); | |
209 | like($gv_ref->FILE(), qr/b\.t$/, "Testing FILE()"); | |
de64752d NC |
210 | is($gv_ref->SvTYPE(), B::SVt_PVGV, "Test SvTYPE()"); |
211 | is($gv_ref->FLAGS() & B::SVTYPEMASK, B::SVt_PVGV, "Test SVTYPEMASK"); | |
bb1efdce FC |
212 | is($gv_ref->GPFLAGS & B::GPf_ALIASED_SV, 0, 'GPFLAGS are unset'); |
213 | { | |
214 | local *gv = \my $x; | |
215 | is($gv_ref->GPFLAGS & B::GPf_ALIASED_SV, B::GPf_ALIASED_SV, | |
216 | 'GPFLAGS gets GPf_ALIASED_SV set'); | |
217 | } | |
2da668d2 SP |
218 | |
219 | # The following return B::SPECIALs. | |
220 | is(ref B::sv_yes(), "B::SPECIAL", "B::sv_yes()"); | |
221 | is(ref B::sv_no(), "B::SPECIAL", "B::sv_no()"); | |
222 | is(ref B::sv_undef(), "B::SPECIAL", "B::sv_undef()"); | |
a462fa00 DD |
223 | SKIP: { |
224 | skip('no fork', 1) | |
225 | unless ($Config::Config{d_fork} or $Config::Config{d_pseudofork}); | |
226 | my $pid; | |
bdbabc4e | 227 | pipe my $r, my $w or die "Can't pipe: $!";; |
a462fa00 | 228 | if ($pid = fork) { |
bdbabc4e JK |
229 | close $w; |
230 | my $type = <$r>; | |
231 | close $r; | |
a462fa00 | 232 | waitpid($pid,0); |
bdbabc4e | 233 | is($type, "B::SPECIAL", "special SV table works after psuedofork"); |
a462fa00 DD |
234 | } |
235 | else { | |
bdbabc4e JK |
236 | close $r; |
237 | $|++; | |
238 | print $w ref B::svref_2object(\(!!0)); | |
239 | close $w; | |
a462fa00 DD |
240 | exit; |
241 | } | |
242 | } | |
2da668d2 SP |
243 | |
244 | # More utility functions | |
245 | is(B::ppname(0), "pp_null", "Testing ppname (this might break if opnames.h is changed)"); | |
246 | is(B::opnumber("null"), 0, "Testing opnumber with opname (null)"); | |
247 | is(B::opnumber("pp_null"), 0, "Testing opnumber with opname (pp_null)"); | |
b45732d6 NC |
248 | { |
249 | my $hash = B::hash("wibble"); | |
250 | like($hash, qr/\A0x[0-9a-f]+\z/, "Testing B::hash(\"wibble\")"); | |
251 | unlike($hash, qr/\A0x0+\z/, "Testing B::hash(\"wibble\")"); | |
252 | ||
bb1ca2d4 YO |
253 | SKIP: { |
254 | skip "Nulls don't hash to the same bucket regardless of length with this PERL_HASH implementation", 20 | |
255 | if B::hash("") ne B::hash("\0" x 19); | |
256 | like(B::hash("\0" x $_), qr/\A0x0+\z/, "Testing B::hash(\"0\" x $_)") | |
257 | for 0..19; | |
258 | } | |
8c5b7c71 NC |
259 | |
260 | $hash = eval {B::hash(chr 256)}; | |
261 | is($hash, undef, "B::hash() refuses non-octets"); | |
262 | like($@, qr/^Wide character in subroutine entry/); | |
263 | ||
264 | $hash = B::hash(chr 163); | |
265 | my $str = chr(163) . chr 256; | |
266 | chop $str; | |
267 | is(B::hash($str), $hash, 'B::hash() with chr 128-256 is well-behaved'); | |
b45732d6 | 268 | } |
f9a20969 NC |
269 | { |
270 | is(B::cstring(undef), '0', "Testing B::cstring(undef)"); | |
271 | is(B::perlstring(undef), '0', "Testing B::perlstring(undef)"); | |
272 | ||
273 | my @common = map {eval $_, $_} | |
574fde55 NC |
274 | '"wibble"', '"\""', '"\'"', '"\\\\"', '"\\n\\r\\t\\b\\a\\f"', '"\000"', |
275 | '"\000\000"', '"\000Bing\000"', ord 'N' == 78 ? '"\\177"' : (); | |
f9a20969 NC |
276 | |
277 | my $oct = sprintf "\\%03o", ord '?'; | |
278 | my @tests = (@common, '$_', '"$_"', '@_', '"@_"', '??N', qq{"$oct?N"}, | |
279 | ord 'N' == 78 ? (chr 11, '"\v"'): ()); | |
280 | while (my ($test, $expect) = splice @tests, 0, 2) { | |
281 | is(B::cstring($test), $expect, "B::cstring($expect)"); | |
282 | } | |
283 | ||
284 | @tests = (@common, '$_', '"\$_"', '@_', '"\@_"', '??N', '"??N"', | |
285 | chr 256, '"\x{100}"', chr 65536, '"\x{10000}"', | |
286 | ord 'N' == 78 ? (chr 11, '"\013"'): ()); | |
287 | while (my ($test, $expect) = splice @tests, 0, 2) { | |
288 | is(B::perlstring($test), $expect, "B::perlstring($expect)"); | |
289 | utf8::upgrade $test; | |
4215ab17 | 290 | $expect =~ s/\\b/sprintf("\\x{%x}", utf8::unicode_to_native(8))/eg; |
f9a20969 NC |
291 | $expect =~ s/\\([0-7]{3})/sprintf "\\x\{%x\}", oct $1/eg; |
292 | is(B::perlstring($test), $expect, "B::perlstring($expect) (Unicode)"); | |
293 | } | |
294 | } | |
01c3a485 | 295 | { |
574fde55 NC |
296 | my @tests = ((map {eval(qq{"$_"}), $_} '\\n', '\\r', '\\t', |
297 | '\\b', '\\a', '\\f', '\\000', '\\\'', '?'), '"', '"', | |
298 | ord 'N' == 78 ? (chr 11, '\v', "\177", '\\177') : ()); | |
01c3a485 NC |
299 | |
300 | while (my ($test, $expect) = splice @tests, 0, 2) { | |
301 | is(B::cchar($test), "'${expect}'", "B::cchar(qq{$expect})"); | |
302 | } | |
303 | } | |
304 | ||
2da668d2 SP |
305 | is(B::class(bless {}, "Wibble::Bibble"), "Bibble", "Testing B::class()"); |
306 | is(B::cast_I32(3.14), 3, "Testing B::cast_I32()"); | |
32b17be1 DM |
307 | is(B::opnumber("chop"), $] >= 5.015 ? 39 : 38, |
308 | "Testing opnumber with opname (chop)"); | |
5ce57cc0 JJ |
309 | |
310 | { | |
311 | no warnings 'once'; | |
312 | my $sg = B::sub_generation(); | |
e1a479c5 | 313 | *UNIVERSAL::hand_waving = sub { }; |
5ce57cc0 JJ |
314 | ok( $sg < B::sub_generation, "sub_generation increments" ); |
315 | } | |
316 | ||
3aaeec97 | 317 | like( B::amagic_generation, qr/^\d+\z/, "amagic_generation" ); |
f9a20969 | 318 | |
fdbd1d64 NC |
319 | is(B::svref_2object(sub {})->ROOT->ppaddr, 'PL_ppaddr[OP_LEAVESUB]', |
320 | 'OP->ppaddr'); | |
321 | ||
512ba29b FC |
322 | # This one crashes from perl 5.8.9 to B 1.24 (perl 5.13.6): |
323 | B::svref_2object(sub{y/\x{100}//})->ROOT->first->first->sibling->sv; | |
324 | ok 1, 'B knows that UTF trans is a padop in 5.8.9, not an svop'; | |
325 | ||
b852bf25 | 326 | { |
619dadb5 FC |
327 | my $o = B::svref_2object(sub{0;0})->ROOT->first->first; |
328 | # Make sure we are testing what we think we are testing. If these two | |
329 | # fail, tweak the test to find a nulled cop a different way. | |
330 | is $o->name, "null", 'first op of sub{0;0} is a null'; | |
331 | is B::ppname($o->targ),'pp_nextstate','first op of sub{0;0} was a cop'; | |
332 | # Test its class | |
333 | is B::class($o), "COP", 'nulled cops are of class COP'; | |
334 | } | |
335 | ||
336 | { | |
b852bf25 FR |
337 | format FOO = |
338 | foo | |
339 | . | |
340 | my $f = B::svref_2object(*FOO{FORMAT}); | |
341 | isa_ok $f, 'B::FM'; | |
342 | can_ok $f, 'LINES'; | |
343 | } | |
344 | ||
d9cd2aeb FC |
345 | is B::safename("\cLAST_FH"), "^LAST_FH", 'basic safename test'; |
346 | ||
a60c099b | 347 | my $sub1 = sub {die}; |
99225839 FC |
348 | { no warnings 'once'; no strict; *Peel:: = *{"Pe\0e\x{142}::"} } |
349 | my $sub2 = eval 'package Peel; sub {die}'; | |
a60c099b | 350 | my $cop = B::svref_2object($sub1)->ROOT->first->first; |
99225839 | 351 | my $bobby = B::svref_2object($sub2)->ROOT->first->first; |
a60c099b FC |
352 | is $cop->stash->object_2svref, \%main::, 'COP->stash'; |
353 | is $cop->stashpv, 'main', 'COP->stashpv'; | |
82aeefe1 DM |
354 | |
355 | SKIP: { | |
356 | skip "no nulls in packages before 5.17", 1 if $] < 5.017; | |
357 | is $bobby->stashpv, "Pe\0e\x{142}", 'COP->stashpv with utf8 and nulls'; | |
358 | } | |
359 | ||
360 | SKIP: { | |
361 | skip "no stashoff", 2 if $] < 5.017 || !$Config::Config{useithreads}; | |
99225839 FC |
362 | like $cop->stashoff, qr/^[1-9]\d*\z/a, 'COP->stashoff'; |
363 | isnt $cop->stashoff, $bobby->stashoff, | |
364 | 'different COP->stashoff for different stashes'; | |
a60c099b FC |
365 | } |
366 | ||
429ba3b2 FC |
367 | my $pmop = B::svref_2object(sub{ qr/fit/ })->ROOT->first->first->sibling; |
368 | $regexp = $pmop->pmregexp; | |
369 | is B::class($regexp), 'REGEXP', 'B::PMOP::pmregexp returns a regexp'; | |
370 | is $regexp->precomp, 'fit', 'pmregexp returns the right regexp'; | |
371 | ||
71324a3b DM |
372 | |
373 | # Test $B::overlay | |
374 | { | |
375 | my $methods = { | |
376 | BINOP => [ qw(last) ], | |
377 | COP => [ qw(arybase cop_seq file filegv hints hints_hash io | |
378 | label line stash stashpv | |
379 | stashoff warnings) ], | |
380 | LISTOP => [ qw(children) ], | |
381 | LOGOP => [ qw(other) ], | |
382 | LOOP => [ qw(lastop nextop redoop) ], | |
383 | OP => [ qw(desc flags name next opt ppaddr private sibling | |
384 | size spare targ type) ], | |
385 | PADOP => [ qw(gv padix sv) ], | |
386 | PMOP => [ qw(code_list pmflags pmoffset pmreplroot pmreplstart pmstash pmstashpv precomp reflags) ], | |
387 | PVOP => [ qw(pv) ], | |
388 | SVOP => [ qw(gv sv) ], | |
389 | UNOP => [ qw(first) ], | |
390 | }; | |
391 | ||
392 | my $overlay = {}; | |
393 | my $op = B::svref_2object(sub { my $x = 1 })->ROOT; | |
394 | ||
395 | for my $class (sort keys %$methods) { | |
396 | for my $meth (@{$methods->{$class}}) { | |
397 | my $full = "B::${class}::$meth"; | |
398 | die "Duplicate method '$full'\n" | |
399 | if grep $_ eq $full, @{$overlay->{$meth}}; | |
400 | push @{$overlay->{$meth}}, "B::${class}::$meth"; | |
401 | } | |
402 | } | |
403 | ||
404 | { | |
405 | local $B::overlay; # suppress 'used once' warning | |
406 | local $B::overlay = { $$op => $overlay }; | |
407 | ||
408 | for my $class (sort keys %$methods) { | |
409 | bless $op, "B::$class"; # naughty | |
410 | for my $meth (@{$methods->{$class}}) { | |
411 | if ($op->can($meth)) { | |
412 | my $list = $op->$meth; | |
413 | ok(defined $list | |
414 | && ref($list) eq "ARRAY" | |
415 | && grep($_ eq "B::${class}::$meth", @$list), | |
416 | "overlay: B::$class $meth"); | |
417 | } | |
418 | else { | |
419 | pass("overlay: B::$class $meth (skipped; no method)"); | |
420 | } | |
421 | } | |
422 | } | |
423 | } | |
424 | # B::overlay should be disabled again here | |
425 | is($op->name, "leavesub", "overlay: orig name"); | |
426 | } | |
427 | ||
486b1e7f TC |
428 | { # [perl #118525] |
429 | { | |
430 | sub foo {} | |
431 | my $cv = B::svref_2object(\&foo); | |
432 | ok($cv, "make a B::CV from a non-anon sub reference"); | |
433 | isa_ok($cv, "B::CV"); | |
434 | my $gv = $cv->GV; | |
435 | ok($gv, "we get a GV from a GV on a normal sub"); | |
436 | isa_ok($gv, "B::GV"); | |
437 | is($gv->NAME, "foo", "check the GV name"); | |
438 | SKIP: | |
439 | { # do we need these version checks? | |
440 | skip "no HEK before 5.18", 1 if $] < 5.018; | |
441 | is($cv->NAME_HEK, undef, "no hek for a global sub"); | |
442 | } | |
443 | } | |
444 | ||
445 | SKIP: | |
446 | { | |
447 | skip "no HEK before 5.18", 4 if $] < 5.018; | |
448 | eval <<'EOS' | |
449 | { | |
450 | use feature 'lexical_subs'; | |
451 | no warnings 'experimental::lexical_subs'; | |
452 | my sub bar {}; | |
453 | my $cv = B::svref_2object(\&bar); | |
454 | ok($cv, "make a B::CV from a lexical sub reference"); | |
455 | isa_ok($cv, "B::CV"); | |
486b1e7f TC |
456 | my $hek = $cv->NAME_HEK; |
457 | is($hek, "bar", "check the NAME_HEK"); | |
ae77754a FC |
458 | my $gv = $cv->GV; |
459 | isa_ok($gv, "B::GV", "GV on a lexical sub"); | |
486b1e7f TC |
460 | } |
461 | 1; | |
462 | EOS | |
463 | or die "lexical_subs test failed to compile: $@"; | |
464 | } | |
465 | } | |
466 | ||
cc54be84 | 467 | { # [perl #120535] |
cc54be84 TC |
468 | my %h = ( "\x{100}" => 1 ); |
469 | my $b = B::svref_2object(\%h); | |
470 | my ($k, $v) = $b->ARRAY; | |
471 | is($k, "\x{100}", "check utf8 preserved by B::HV::ARRAY"); | |
472 | } | |
473 | ||
29e61fd9 DM |
474 | # test op_parent |
475 | ||
476 | SKIP: { | |
477 | unless ($Config::Config{ccflags} =~ /PERL_OP_PARENT/) { | |
478 | skip "op_parent only present with -DPERL_OP_PARENT builds", 6; | |
479 | } | |
480 | my $lineseq = B::svref_2object(sub{my $x = 1})->ROOT->first; | |
481 | is ($lineseq->type, B::opnumber('lineseq'), | |
482 | 'op_parent: top op is lineseq'); | |
483 | my $first = $lineseq->first; | |
484 | my $second = $first->sibling; | |
485 | is(ref $second->sibling, "B::NULL", 'op_parent: second sibling is null'); | |
486 | is($first->lastsib, 0 , 'op_parent: first sibling: !lastsib'); | |
487 | is($second->lastsib, 1, 'op_parent: second sibling: lastsib'); | |
488 | is($$lineseq, ${$first->parent}, 'op_parent: first sibling okay'); | |
489 | is($$lineseq, ${$second->parent}, 'op_parent: second sibling okay'); | |
490 | } | |
491 | ||
492 | ||
3a23d767 DM |
493 | # make sure ->sv, -gv methods do the right thing on threaded builds |
494 | { | |
495 | ||
496 | # for some reason B::walkoptree only likes a sub name, not a code ref | |
497 | my ($gv, $sv); | |
498 | sub gvsv_const { | |
499 | # make the early pad slots something unlike a threaded const or | |
500 | # gvsv | |
501 | my ($dummy1, $dummy2, $dummy3, $dummy4) = qw(foo1 foo2 foo3 foo4); | |
502 | my $self = shift; | |
503 | if ($self->name eq 'gvsv') { | |
504 | $gv = $self->gv; | |
505 | } | |
506 | elsif ($self->name eq 'const') { | |
507 | $sv = $self->sv; | |
508 | } | |
509 | }; | |
510 | ||
511 | B::walkoptree(B::svref_2object(sub {our $x = 1})->ROOT, "::gvsv_const"); | |
512 | ok(defined $gv, "gvsv->gv seen"); | |
513 | ok(defined $sv, "const->sv seen"); | |
514 | if ($Config::Config{useithreads}) { | |
515 | # should get NULLs | |
516 | is(ref($gv), "B::SPECIAL", "gvsv->gv is special"); | |
517 | is(ref($sv), "B::SPECIAL", "const->sv is special"); | |
518 | is($$gv, 0, "gvsv->gv special is 0 (NULL)"); | |
519 | is($$sv, 0, "const->sv special is 0 (NULL)"); | |
520 | } | |
521 | else { | |
522 | is(ref($gv), "B::GV", "gvsv->gv is GV"); | |
523 | is(ref($sv), "B::IV", "const->sv is IV"); | |
524 | pass(); | |
525 | pass(); | |
526 | } | |
527 | ||
528 | } | |
529 | ||
530 | ||
1d7705d5 FC |
531 | # Some pad tests |
532 | { | |
533 | my $sub = sub { my main $a; CORE::state @b; our %c }; | |
534 | my $padlist = B::svref_2object($sub)->PADLIST; | |
535 | is $padlist->MAX, 1, 'padlist MAX'; | |
536 | my @array = $padlist->ARRAY; | |
537 | is @array, 2, 'two items from padlist ARRAY'; | |
538 | is ${$padlist->ARRAYelt(0)}, ${$array[0]}, | |
539 | 'ARRAYelt(0) is first item from ARRAY'; | |
540 | is ${$padlist->ARRAYelt(1)}, ${$array[1]}, | |
541 | 'ARRAYelt(1) is second item from ARRAY'; | |
542 | is ${$padlist->NAMES}, ${$array[0]}, | |
543 | 'NAMES is first item from ARRAY'; | |
544 | my @names = $array[0]->ARRAY; | |
545 | cmp_ok @names, ">=", 4, 'at least 4 pad names'; | |
546 | is join(" ", map($_->PV//"undef",@names[0..3])), 'undef $a @b %c', | |
547 | 'pad name PVs'; | |
548 | ||
549 | my @closures; | |
550 | for (1,2) { push @closures, sub { sub { @closures } } } | |
551 | my $sub1 = B::svref_2object($closures[0]); | |
552 | my $sub2 = B::svref_2object($closures[1]); | |
553 | is $sub2->PADLIST->id, $sub1->PADLIST->id, 'padlist id'; | |
554 | $sub1 = B::svref_2object(my $lr = $closures[0]()); | |
555 | $sub2 = B::svref_2object(my $lr2= $closures[1]()); | |
556 | is $sub2->PADLIST->outid, $sub1->PADLIST->outid, 'padlist outid'; | |
557 | } | |
558 | ||
559 | ||
f9a20969 | 560 | done_testing(); |