summaryrefslogtreecommitdiff
path: root/ext/B/t/b.t
blob: 5bfd3f1f1a472559fdf6d2a405e10f8b8f2a13ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#!./perl

BEGIN {
    unshift @INC, 't';
    require Config;
    if (($Config::Config{'extensions'} !~ /\bB\b/) ){
        print "1..0 # Skip -- Perl configured without B module\n";
        exit 0;
    }
}

$|  = 1;
use warnings;
use strict;
use Test::More;

BEGIN { use_ok( 'B' ); }


package Testing::Symtable;
use vars qw($This @That %wibble $moo %moo);
my $not_a_sym = 'moo';

sub moo { 42 }
sub car { 23 }


package Testing::Symtable::Foo;
sub yarrow { "Hock" }

package Testing::Symtable::Bar;
sub hock { "yarrow" }

package main;
use vars qw(%Subs);
local %Subs = ();
B::walksymtable(\%Testing::Symtable::, 'find_syms', sub { $_[0] =~ /Foo/ },
                'Testing::Symtable::');

sub B::GV::find_syms {
    my($symbol) = @_;

    $main::Subs{$symbol->STASH->NAME . '::' . $symbol->NAME}++;
}

my @syms = map { 'Testing::Symtable::'.$_ } qw(This That wibble moo car
                                               BEGIN);
push @syms, "Testing::Symtable::Foo::yarrow";

# Make sure we hit all the expected symbols.
ok( join('', sort @syms) eq join('', sort keys %Subs), 'all symbols found' );

# Make sure we only hit them each once.
ok( (!grep $_ != 1, values %Subs), '...and found once' );

# Tests for MAGIC / MOREMAGIC
ok( B::svref_2object(\$.)->MAGIC->TYPE eq "\0", '$. has \0 magic' );
{
    my $e = '';
    local $SIG{__DIE__} = sub { $e = $_[0] };
    # Used to dump core, bug #16828
    eval { B::svref_2object(\$.)->MAGIC->MOREMAGIC->TYPE; };
    like( $e, qr/Can't call method "TYPE" on an undefined value/, 
	'$. has no more magic' );
}

{
    my $pie = 'Good';
    # This needs to be a package variable, as vars in the pad have some flags.
    my $r = B::svref_2object(\$::data2);
    is($r->FLAGS(), 0, "uninitialised package variable has flags of 0");
    is($r->SvTYPE(), 0, "uninitialised package variable has type 0");
    is($r->POK(), 0, "POK false");
    is($r->ROK(), 0, "ROK false");
    is($r->MAGICAL(), 0, "MAGICAL false");
    $::data2 = $pie;
    isnt($r->FLAGS(), 0, "initialised package variable has nonzero flags");
    isnt($r->SvTYPE(), 0, "initialised package variable has nonzero type");
    isnt($r->POK(), 0, "POK true");
    is($r->ROK(), 0, "ROK false");
    is($r->MAGICAL(), 0, "MAGICAL false");

    $::data2 = substr $pie, 0, 1;
    isnt($r->FLAGS(), 0, "initialised package variable has nonzero flags");
    isnt($r->SvTYPE(), 0, "initialised package variable has nonzero type");
    isnt($r->POK(), 0, "POK true");
    is($r->ROK(), 0, "ROK false");
    is($r->MAGICAL(), 0, "MAGICAL true");

    $::data2 = \$pie;
    isnt($r->FLAGS(), 0, "initialised package variable has nonzero flags");
    isnt($r->SvTYPE(), 0, "initialised package variable has nonzero type");
    is($r->POK(), 0, "POK false");
    isnt($r->ROK(), 0, "ROK true");
    is($r->MAGICAL(), 0, "MAGICAL false");

    is($r->REFCNT(), 1, "Reference count is 1");
    {
	my $ref = \$::data2;
	is($r->REFCNT(), 2, "Second reference");
    }
    is($r->REFCNT(), 1, "Reference count is 1");

}

my $r = qr/foo/;
my $obj = B::svref_2object($r);
my $regexp =  ($] < 5.011) ? $obj->MAGIC : $obj;
ok($regexp->precomp() eq 'foo', 'Get string from qr//');
like($regexp->REGEX(), qr/\d+/, "REGEX() returns numeric value");
my $iv = 1;
my $iv_ref = B::svref_2object(\$iv);
is(ref $iv_ref, "B::IV", "Test B:IV return from svref_2object");
is($iv_ref->REFCNT, 1, "Test B::IV->REFCNT");
# Flag tests are needed still
#diag $iv_ref->FLAGS();
my $iv_ret = $iv_ref->object_2svref();
is(ref $iv_ret, "SCALAR", "Test object_2svref() return is SCALAR");
is($$iv_ret, $iv, "Test object_2svref()");
is($iv_ref->int_value, $iv, "Test int_value()");
is($iv_ref->IV, $iv, "Test IV()");
is($iv_ref->IVX(), $iv, "Test IVX()");
is($iv_ref->UVX(), $iv, "Test UVX()");
is(eval { $iv_ref->RV() }, undef, 'Test RV() on IV');
like($@, qr/argument is not SvROK/, 'Test RV() IV');
$iv = \"Pie";
my $val = eval { $iv_ref->RV() };
is(ref $val, 'B::PV', 'Test RV() on a reference');
is($val->PV(), 'Pie', 'Value expected');
is($@, '', "Test RV()");

my $pv = "Foo";
my $pv_ref = B::svref_2object(\$pv);
is(ref $pv_ref, "B::PV", "Test B::PV return from svref_2object");
is($pv_ref->REFCNT, 1, "Test B::PV->REFCNT");
# Flag tests are needed still
#diag $pv_ref->FLAGS();
my $pv_ret = $pv_ref->object_2svref();
is(ref $pv_ret, "SCALAR", "Test object_2svref() return is SCALAR");
is($$pv_ret, $pv, "Test object_2svref()");
is($pv_ref->PV(), $pv, "Test PV()");
is(eval { $pv_ref->RV() }, undef, 'Test RV() on PV');
like($@, qr/argument is not SvROK/, 'Test RV() on PV');
is($pv_ref->PVX(), $pv, "Test PVX()");
$pv = \"Pie";
$val = eval { $pv_ref->RV() };
is(ref $val, 'B::PV', 'Test RV() on a reference');
is($val->PV(), 'Pie', 'Value expected');
is($@, '', "Test RV()");

my $nv = 1.1;
my $nv_ref = B::svref_2object(\$nv);
is(ref $nv_ref, "B::NV", "Test B::NV return from svref_2object");
is($nv_ref->REFCNT, 1, "Test B::NV->REFCNT");
# Flag tests are needed still
#diag $nv_ref->FLAGS();
my $nv_ret = $nv_ref->object_2svref();
is(ref $nv_ret, "SCALAR", "Test object_2svref() return is SCALAR");
is($$nv_ret, $nv, "Test object_2svref()");
is($nv_ref->NV, $nv, "Test NV()");
is($nv_ref->NVX(), $nv, "Test NVX()");
is(eval { $nv_ref->RV() }, undef, 'Test RV() on NV');
like($@, qr/Can't locate object method "RV" via package "B::NV"/,
     'Test RV() on NV');

my $null = undef;
my $null_ref = B::svref_2object(\$null);
is(ref $null_ref, "B::NULL", "Test B::NULL return from svref_2object");
is($null_ref->REFCNT, 1, "Test B::NULL->REFCNT");
# Flag tests are needed still
#diag $null_ref->FLAGS();
my $null_ret = $nv_ref->object_2svref();
is(ref $null_ret, "SCALAR", "Test object_2svref() return is SCALAR");
is($$null_ret, $nv, "Test object_2svref()");

my $RV_class = $] >= 5.011 ? 'B::IV' : 'B::RV';
my $cv = sub{ 1; };
my $cv_ref = B::svref_2object(\$cv);
is($cv_ref->REFCNT, 1, "Test $RV_class->REFCNT");
is(ref $cv_ref, "$RV_class",
   "Test $RV_class return from svref_2object - code");
my $cv_ret = $cv_ref->object_2svref();
is(ref $cv_ret, "REF", "Test object_2svref() return is REF");
is($$cv_ret, $cv, "Test object_2svref()");

my $av = [];
my $av_ref = B::svref_2object(\$av);
is(ref $av_ref, "$RV_class",
   "Test $RV_class return from svref_2object - array");

my $hv = [];
my $hv_ref = B::svref_2object(\$hv);
is(ref $hv_ref, "$RV_class",
   "Test $RV_class return from svref_2object - hash");

local *gv = *STDOUT;
my $gv_ref = B::svref_2object(\*gv);
is(ref $gv_ref, "B::GV", "Test B::GV return from svref_2object");
ok(! $gv_ref->is_empty(), "Test is_empty()");
ok($gv_ref->isGV_with_GP(), "Test isGV_with_GP()");
is($gv_ref->NAME(), "gv", "Test NAME()");
is($gv_ref->SAFENAME(), "gv", "Test SAFENAME()");
like($gv_ref->FILE(), qr/b\.t$/, "Testing FILE()");
is($gv_ref->SvTYPE(), B::SVt_PVGV, "Test SvTYPE()");
is($gv_ref->FLAGS() & B::SVTYPEMASK, B::SVt_PVGV, "Test SVTYPEMASK");

# The following return B::SPECIALs.
is(ref B::sv_yes(), "B::SPECIAL", "B::sv_yes()");
is(ref B::sv_no(), "B::SPECIAL", "B::sv_no()");
is(ref B::sv_undef(), "B::SPECIAL", "B::sv_undef()");

# More utility functions
is(B::ppname(0), "pp_null", "Testing ppname (this might break if opnames.h is changed)");
is(B::opnumber("null"), 0, "Testing opnumber with opname (null)");
is(B::opnumber("pp_null"), 0, "Testing opnumber with opname (pp_null)");
{
    my $hash = B::hash("wibble");
    like($hash, qr/\A0x[0-9a-f]+\z/, "Testing B::hash(\"wibble\")");
    unlike($hash, qr/\A0x0+\z/, "Testing B::hash(\"wibble\")");

    like(B::hash("\0" x $_), qr/\A0x0+\z/, "Testing B::hash(\"0\" x $_)")
	 for 0..19;

    $hash = eval {B::hash(chr 256)};
    is($hash, undef, "B::hash() refuses non-octets");
    like($@, qr/^Wide character in subroutine entry/);

    $hash = B::hash(chr 163);
    my $str = chr(163) . chr 256;
    chop $str;
    is(B::hash($str), $hash, 'B::hash() with chr 128-256 is well-behaved');
}
{
    is(B::cstring(undef), '0', "Testing B::cstring(undef)");
    is(B::perlstring(undef), '0', "Testing B::perlstring(undef)");

    my @common = map {eval $_, $_}
	'"wibble"', '"\""', '"\'"', '"\\\\"', '"\\n\\r\\t\\b\\a\\f"', '"\000"',
	    '"\000\000"', '"\000Bing\000"', ord 'N' == 78 ? '"\\177"' : ();

    my $oct = sprintf "\\%03o", ord '?';
    my @tests = (@common, '$_', '"$_"', '@_', '"@_"', '??N', qq{"$oct?N"},
		 ord 'N' == 78 ? (chr 11, '"\v"'): ());
    while (my ($test, $expect) = splice @tests, 0, 2) {
	is(B::cstring($test), $expect, "B::cstring($expect)");
    }

    @tests = (@common, '$_', '"\$_"', '@_', '"\@_"', '??N', '"??N"',
	      chr 256, '"\x{100}"', chr 65536, '"\x{10000}"',
	      ord 'N' == 78 ? (chr 11, '"\013"'): ());
    while (my ($test, $expect) = splice @tests, 0, 2) {
	is(B::perlstring($test), $expect, "B::perlstring($expect)");
	utf8::upgrade $test;
	$expect =~ s/\\b/\\x\{8\}/g;
	$expect =~ s/\\([0-7]{3})/sprintf "\\x\{%x\}", oct $1/eg;
	is(B::perlstring($test), $expect, "B::perlstring($expect) (Unicode)");
    }
}
{
    my @tests = ((map {eval(qq{"$_"}), $_} '\\n', '\\r', '\\t',
		  '\\b', '\\a', '\\f', '\\000', '\\\'', '?'), '"', '"',
		 ord 'N' == 78 ? (chr 11, '\v', "\177", '\\177') : ());

    while (my ($test, $expect) = splice @tests, 0, 2) {
	is(B::cchar($test), "'${expect}'", "B::cchar(qq{$expect})");
    }
}

is(B::class(bless {}, "Wibble::Bibble"), "Bibble", "Testing B::class()");
is(B::cast_I32(3.14), 3, "Testing B::cast_I32()");
is(B::opnumber("chop"), 38, "Testing opnumber with opname (chop)");

{
    no warnings 'once';
    my $sg = B::sub_generation();
    *UNIVERSAL::hand_waving = sub { };
    ok( $sg < B::sub_generation, "sub_generation increments" );
}

{
    my $ag = B::amagic_generation();
    {

        package Whatever;
        require overload;
        overload->import( '""' => sub {"What? You want more?!"} );
    }
    ok( $ag < B::amagic_generation, "amagic_generation increments" );
}

is(B::svref_2object(sub {})->ROOT->ppaddr, 'PL_ppaddr[OP_LEAVESUB]',
   'OP->ppaddr');

# This one crashes from perl 5.8.9 to B 1.24 (perl 5.13.6):
B::svref_2object(sub{y/\x{100}//})->ROOT->first->first->sibling->sv;
ok 1, 'B knows that UTF trans is a padop in 5.8.9, not an svop';

done_testing();