xref: /openbsd-src/gnu/usr.bin/perl/lib/builtin.t (revision 5486feefcc8cb79b19e014ab332cc5dfd05b3b33)
1#!./perl
2
3BEGIN {
4    chdir 't' if -d 't';
5    require './test.pl';
6    set_up_inc('../lib');
7}
8
9use v5.36;
10no warnings 'experimental::builtin';
11
12package FetchStoreCounter {
13    sub TIESCALAR($class, @args) { bless \@args, $class }
14
15    sub FETCH($self)    { $self->[0]->$*++ }
16    sub STORE($self, $) { $self->[1]->$*++ }
17}
18
19# booleans
20{
21    use builtin qw( true false is_bool );
22
23    ok(true, 'true is true');
24    ok(!false, 'false is false');
25
26    ok(is_bool(true), 'true is bool');
27    ok(is_bool(false), 'false is bool');
28    ok(!is_bool(undef), 'undef is not bool');
29    ok(!is_bool(1), '1 is not bool');
30    ok(!is_bool(""), 'empty is not bool');
31
32    my $truevar  = (5 == 5);
33    my $falsevar = (5 == 6);
34
35    ok(is_bool($truevar), '$truevar is bool');
36    ok(is_bool($falsevar), '$falsevar is bool');
37
38    ok(is_bool(is_bool(true)), 'is_bool true is bool');
39    ok(is_bool(is_bool(123)),  'is_bool false is bool');
40
41    # Invokes magic
42
43    tie my $tied, FetchStoreCounter => (\my $fetchcount, \my $storecount);
44
45    my $_dummy = is_bool($tied);
46    is($fetchcount, 1, 'is_bool() invokes FETCH magic');
47
48    $tied = is_bool(false);
49    is($storecount, 1, 'is_bool() invokes STORE magic');
50
51    is(prototype(\&builtin::is_bool), '$', 'is_bool prototype');
52}
53
54# float constants
55{
56    use builtin qw( inf nan );
57
58    ok(inf, 'inf is true');
59    ok(inf > 1E10, 'inf is bigger than 1E10');
60    ok(inf == inf, 'inf is equal to inf');
61    ok(inf == inf + 1, 'inf is equal to inf + 1');
62
63    # Invoke the real XSUB
64    my $inf = ( \&builtin::inf )->();
65    ok($inf == $inf + 1, 'inf returned by real xsub');
66
67    ok(nan != nan, 'NaN is not equal to NaN');
68
69    my $nan = ( \&builtin::nan )->();
70    ok($nan != $nan, 'NaN returned by real xsub');
71}
72
73# weakrefs
74{
75    use builtin qw( is_weak weaken unweaken );
76
77    my $arr = [];
78    my $ref = $arr;
79
80    ok(!is_weak($ref), 'ref is not weak initially');
81
82    weaken($ref);
83    ok(is_weak($ref), 'ref is weak after weaken()');
84
85    unweaken($ref);
86    ok(!is_weak($ref), 'ref is not weak after unweaken()');
87
88    weaken($ref);
89    undef $arr;
90    is($ref, undef, 'ref is now undef after arr is cleared');
91
92    is(prototype(\&builtin::weaken), '$', 'weaken prototype');
93    is(prototype(\&builtin::unweaken), '$', 'unweaken prototype');
94    is(prototype(\&builtin::is_weak), '$', 'is_weak prototype');
95}
96
97# reference queries
98{
99    use builtin qw( refaddr reftype blessed );
100
101    my $arr = [];
102    my $obj = bless [], "Object";
103
104    is(refaddr($arr),        $arr+0, 'refaddr yields same as ref in numeric context');
105    is(refaddr("not a ref"), undef,  'refaddr yields undef for non-reference');
106
107    is(reftype($arr),        "ARRAY", 'reftype yields type string');
108    is(reftype($obj),        "ARRAY", 'reftype yields basic container type for blessed object');
109    is(reftype("not a ref"), undef,   'reftype yields undef for non-reference');
110
111    is(blessed($arr), undef, 'blessed yields undef for non-object');
112    is(blessed($obj), "Object", 'blessed yields package name for object');
113
114    # blessed() as a boolean
115    is(blessed($obj) ? "YES" : "NO", "YES", 'blessed in boolean context still works');
116
117    # blessed() appears false as a boolean on package "0"
118    is(blessed(bless [], "0") ? "YES" : "NO", "NO", 'blessed in boolean context handles "0" cornercase');
119
120    is(prototype(\&builtin::blessed), '$', 'blessed prototype');
121    is(prototype(\&builtin::refaddr), '$', 'refaddr prototype');
122    is(prototype(\&builtin::reftype), '$', 'reftype prototype');
123}
124
125# created_as_...
126{
127    use builtin qw( created_as_string created_as_number );
128
129    # some literal constants
130    ok(!created_as_string(undef), 'undef created as !string');
131    ok(!created_as_number(undef), 'undef created as !number');
132
133    ok( created_as_string("abc"), 'abc created as string');
134    ok(!created_as_number("abc"), 'abc created as number');
135
136    ok(!created_as_string(123),   '123 created as !string');
137    ok( created_as_number(123),   '123 created as !number');
138
139    ok(!created_as_string(1.23),   '1.23 created as !string');
140    ok( created_as_number(1.23),   '1.23 created as !number');
141
142    ok(!created_as_string([]),    '[] created as !string');
143    ok(!created_as_number([]),    '[] created as !number');
144
145    ok(!created_as_string(builtin::true), 'true created as !string');
146    ok(!created_as_number(builtin::true), 'true created as !number');
147
148    ok(builtin::is_bool(created_as_string(0)), 'created_as_string returns bool');
149    ok(builtin::is_bool(created_as_number(0)), 'created_as_number returns bool');
150
151    # variables
152    my $just_pv = "def";
153    ok( created_as_string($just_pv), 'def created as string');
154    ok(!created_as_number($just_pv), 'def created as number');
155
156    my $just_iv = 456;
157    ok(!created_as_string($just_iv), '456 created as string');
158    ok( created_as_number($just_iv), '456 created as number');
159
160    my $just_nv = 4.56;
161    ok(!created_as_string($just_nv), '456 created as string');
162    ok( created_as_number($just_nv), '456 created as number');
163
164    # variables reused
165    my $originally_pv = "1";
166    my $pv_as_iv = $originally_pv + 0;
167    ok( created_as_string($originally_pv), 'PV reused as IV created as string');
168    ok(!created_as_number($originally_pv), 'PV reused as IV created as !number');
169    ok(!created_as_string($pv_as_iv), 'New number from PV created as !string');
170    ok( created_as_number($pv_as_iv), 'New number from PV created as number');
171
172    my $originally_iv = 1;
173    my $iv_as_pv = "$originally_iv";
174    ok(!created_as_string($originally_iv), 'IV reused as PV created as !string');
175    ok( created_as_number($originally_iv), 'IV reused as PV created as number');
176    ok( created_as_string($iv_as_pv), 'New string from IV created as string');
177    ok(!created_as_number($iv_as_pv), 'New string from IV created as !number');
178
179    my $originally_nv = 1.1;
180    my $nv_as_pv = "$originally_nv";
181    ok(!created_as_string($originally_nv), 'NV reused as PV created as !string');
182    ok( created_as_number($originally_nv), 'NV reused as PV created as number');
183    ok( created_as_string($nv_as_pv), 'New string from NV created as string');
184    ok(!created_as_number($nv_as_pv), 'New string from NV created as !number');
185
186    # magic
187    local $1;
188    "hello" =~ m/(.*)/;
189    ok(created_as_string($1), 'magic string');
190
191    is(prototype(\&builtin::created_as_string), '$', 'created_as_string prototype');
192    is(prototype(\&builtin::created_as_number), '$', 'created_as_number prototype');
193}
194
195# stringify
196{
197    use builtin qw( stringify );
198
199    is(stringify("abc"), "abc", 'stringify a plain string');
200    is(stringify(123),   "123", 'stringify a number');
201
202    my $aref = [];
203    is(stringify($aref), "$aref", 'stringify an array ref');
204
205    use builtin qw( created_as_string );
206    ok(!ref stringify($aref),               'stringified arrayref is not a ref');
207    ok(created_as_string(stringify($aref)), 'stringified arrayref is created as string');
208
209    package WithOverloadedStringify {
210        use overload '""' => sub { return "STRING" };
211    }
212
213    is(stringify(bless [], "WithOverloadedStringify"), "STRING", 'stringify invokes "" overload');
214}
215
216# ceil, floor
217{
218    use builtin qw( ceil floor );
219
220    cmp_ok(ceil(1.5), '==', 2, 'ceil(1.5) == 2');
221    cmp_ok(floor(1.5), '==', 1, 'floor(1.5) == 1');
222
223    # Invokes magic
224
225    tie my $tied, FetchStoreCounter => (\my $fetchcount, \my $storecount);
226
227    my $_dummy = ceil($tied);
228    is($fetchcount, 1, 'ceil() invokes FETCH magic');
229
230    $tied = ceil(1.1);
231    is($storecount, 1, 'ceil() TARG invokes STORE magic');
232
233    $fetchcount = $storecount = 0;
234    tie $tied, FetchStoreCounter => (\$fetchcount, \$storecount);
235
236    $_dummy = floor($tied);
237    is($fetchcount, 1, 'floor() invokes FETCH magic');
238
239    $tied = floor(1.1);
240    is($storecount, 1, 'floor() TARG invokes STORE magic');
241
242    is(prototype(\&builtin::ceil), '$', 'ceil prototype');
243    is(prototype(\&builtin::floor), '$', 'floor prototype');
244}
245
246# imports are lexical; should not be visible here
247{
248    my $ok = eval 'true()'; my $e = $@;
249    ok(!$ok, 'true() not visible outside of lexical scope');
250    like($e, qr/^Undefined subroutine &main::true called at /, 'failure from true() not visible');
251}
252
253# lexical imports work fine in a variety of situations
254{
255    sub regularfunc {
256        use builtin 'true';
257        return true;
258    }
259    ok(regularfunc(), 'true in regular sub');
260
261    my sub lexicalfunc {
262        use builtin 'true';
263        return true;
264    }
265    ok(lexicalfunc(), 'true in lexical sub');
266
267    my $coderef = sub {
268        use builtin 'true';
269        return true;
270    };
271    ok($coderef->(), 'true in anon sub');
272
273    sub recursefunc {
274        use builtin 'true';
275        return recursefunc() if @_;
276        return true;
277    }
278    ok(recursefunc("rec"), 'true in self-recursive sub');
279
280    my sub recurselexicalfunc {
281        use builtin 'true';
282        return __SUB__->() if @_;
283        return true;
284    }
285    ok(recurselexicalfunc("rec"), 'true in self-recursive lexical sub');
286
287    my $recursecoderef = sub {
288        use builtin 'true';
289        return __SUB__->() if @_;
290        return true;
291    };
292    ok($recursecoderef->("rec"), 'true in self-recursive anon sub');
293}
294
295{
296    use builtin qw( true false );
297
298    my $val = true;
299    cmp_ok($val, $_, !!1, "true is equivalent to !!1 by $_") for qw( eq == );
300    cmp_ok($val, $_,  !0, "true is equivalent to  !0 by $_") for qw( eq == );
301
302    $val = false;
303    cmp_ok($val, $_, !!0, "false is equivalent to !!0 by $_") for qw( eq == );
304    cmp_ok($val, $_,  !1, "false is equivalent to  !1 by $_") for qw( eq == );
305}
306
307# indexed
308{
309    use builtin qw( indexed );
310
311    # We don't have Test::More's is_deeply here
312
313    ok(eq_array([indexed], [] ),
314        'indexed on empty list');
315
316    ok(eq_array([indexed "A"], [0, "A"] ),
317        'indexed on singleton list');
318
319    ok(eq_array([indexed "X" .. "Z"], [0, "X", 1, "Y", 2, "Z"] ),
320        'indexed on 3-item list');
321
322    my @orig = (1..3);
323    $_++ for indexed @orig;
324    ok(eq_array(\@orig, [1 .. 3]), 'indexed copies values, does not alias');
325
326    {
327        my $ok = 1;
328        foreach my ($len, $s) (indexed "", "x", "xx") {
329            length($s) == $len or undef $ok;
330        }
331        ok($ok, 'indexed operates nicely with multivar foreach');
332    }
333
334    {
335        my %hash = indexed "a" .. "e";
336        ok(eq_hash(\%hash, { 0 => "a", 1 => "b", 2 => "c", 3 => "d", 4 => "e" }),
337            'indexed can be used to create hashes');
338    }
339
340    {
341        no warnings 'scalar';
342
343        my $count = indexed 'i', 'ii', 'iii', 'iv';
344        is($count, 8, 'indexed in scalar context yields size of list it would return');
345    }
346
347    is(prototype(\&builtin::indexed), '@', 'indexed prototype');
348}
349
350# Vanilla trim tests
351{
352    use builtin qw( trim );
353
354    is(trim("    Hello world!   ")      , "Hello world!"  , 'trim spaces');
355    is(trim("\tHello world!\t")         , "Hello world!"  , 'trim tabs');
356    is(trim("\n\n\nHello\nworld!\n")    , "Hello\nworld!" , 'trim \n');
357    is(trim("\t\n\n\nHello world!\n \t"), "Hello world!"  , 'trim all three');
358    is(trim("Perl")                     , "Perl"          , 'trim nothing');
359    is(trim('')                         , ""              , 'trim empty string');
360
361    is(prototype(\&builtin::trim), '$', 'trim prototype');
362}
363
364TODO: {
365    my $warn = '';
366    local $SIG{__WARN__} = sub { $warn .= join "", @_; };
367
368    is(builtin::trim(undef), "", 'trim undef');
369    like($warn    , qr/^Use of uninitialized value in subroutine entry at/,
370         'trim undef triggers warning');
371    local $main::TODO = "Currently uses generic value for the name of non-opcode builtins";
372    like($warn    , qr/^Use of uninitialized value in trim at/,
373         'trim undef triggers warning using actual name of builtin');
374}
375
376# Fancier trim tests against a regexp and unicode
377{
378    use builtin qw( trim );
379    my $nbsp = chr utf8::unicode_to_native(0xA0);
380
381    is(trim("   \N{U+2603}       "), "\N{U+2603}", 'trim with unicode content');
382    is(trim("\N{U+2029}foobar\x{2028} "), "foobar",
383            'trim with unicode whitespace');
384    is(trim("$nbsp foobar$nbsp    "), "foobar", 'trim with latin1 whitespace');
385}
386
387# Test on a magical fetching variable
388{
389    use builtin qw( trim );
390
391    my $str3 = "   Hello world!\t";
392    $str3 =~ m/(.+Hello)/;
393    is(trim($1), "Hello", "trim on a magical variable");
394}
395
396# Inplace edit, my, our variables
397{
398    use builtin qw( trim );
399
400    my $str4 = "\t\tHello world!\n\n";
401    $str4 = trim($str4);
402    is($str4, "Hello world!", "trim on an inplace variable");
403
404    our $str2 = "\t\nHello world!\t  ";
405    is(trim($str2), "Hello world!", "trim on an our \$var");
406}
407
408# Lexical export
409{
410    my $name;
411    BEGIN {
412        use builtin qw( export_lexically );
413
414        $name = "message";
415        export_lexically $name => sub { "Hello, world" };
416    }
417
418    is(message(), "Hello, world", 'Lexically exported sub is callable');
419    ok(!__PACKAGE__->can("message"), 'Exported sub is not visible via ->can');
420
421    is($name, "message", '$name argument was not modified by export_lexically');
422
423    our ( $scalar, @array, %hash );
424    BEGIN {
425        use builtin qw( export_lexically );
426
427        export_lexically
428            '$SCALAR' => \$scalar,
429            '@ARRAY'  => \@array,
430            '%HASH'   => \%hash;
431    }
432
433    $::scalar = "value";
434    is($SCALAR, "value", 'Lexically exported scalar is accessible');
435
436    @::array = ('a' .. 'e');
437    is(scalar @ARRAY, 5, 'Lexically exported array is accessible');
438
439    %::hash = (key => "val");
440    is($HASH{key}, "val", 'Lexically exported hash is accessible');
441}
442
443# load_module
444{
445    use builtin qw( load_module );
446    use feature qw( try );
447    my ($ok, $e);
448
449    # Can't really test this sans string eval, as it's a compilation error:
450    eval 'load_module();';
451    $e = $@;
452    ok($e, 'load_module(); fails');
453    like($e, qr/^Not enough arguments for builtin::load_module at/, 'load_module(); fails with correct error');
454    eval 'load_module;';
455    $e = $@;
456    ok($e, 'load_module; fails');
457    like($e, qr/^Not enough arguments for builtin::load_module at/, 'load_module; fails with correct error');
458
459    # Failure to load module croaks
460    try {
461        load_module(undef);
462    } catch ($e) {
463        ok($e, 'load_module(undef) fails');
464        like($e, qr/^Usage: builtin::load_module\(defined string\)/, 'load_module(undef) fails with correct error');
465    };
466    try {
467        load_module(\"Foo");
468    } catch ($e) {
469        ok($e, 'load_module(\"Foo") fails');
470        like($e, qr/^Usage: builtin::load_module\(defined string\)/, 'load_module(\"Foo") fails with correct error');
471    };
472    try {
473        load_module(["Foo"]);
474    } catch ($e) {
475        ok($e, 'load_module(["Foo"]) fails');
476        like($e, qr/^Usage: builtin::load_module\(defined string\)/, 'load_module(["Foo"]) fails with correct error');
477    };
478    try {
479        load_module('5.36');
480    }
481    catch ($e) {
482        ok($e, 'load_module("5.36") fails');
483        like($e, qr/^Can't locate 5[.]36[.]pm in \@INC/, 'load_module("5.36") fails with correct error');
484    };
485    try {
486        load_module('v5.36');
487    }
488    catch ($e) {
489        ok($e, 'load_module("v5.36") fails');
490        like($e, qr/^Can't locate v5[.]36[.]pm in \@INC/, 'load_module("v5.36") fails with correct error');
491    };
492    try {
493        load_module("Dies");
494        fail('load_module("Dies") succeeded!');
495    }
496    catch ($e) {
497        ok($e, 'load_module("Dies") fails');
498        like($e, qr/^Can't locate Dies[.]pm in \@INC/, 'load_module("Dies") fails with correct error');
499    }
500    my $module_name = 'Dies';
501    try {
502        load_module($module_name);
503        fail('load_module($module_name) $module_name=Dies succeeded!');
504    }
505    catch ($e) {
506        ok($e, 'load_module($module_name) $module_name=Dies fails');
507        like($e, qr/^Can't locate Dies[.]pm in \@INC/, 'load_module($module_name) $module_name=Dies fails with correct error');
508    }
509    $module_name =~ m!(\w+)!;
510    try {
511        load_module($1);
512        fail('load_module($1) from $module_name=Dies succeeded!');
513    }
514    catch ($e) {
515        ok($e, 'load_module($1) from $module_name=Dies fails');
516        like($e, qr/^Can't locate Dies[.]pm in \@INC/, 'load_module($1) from $module_name=Dies fails with correct error');
517    }
518    "Dies" =~ m!(\w+)!;
519    try {
520        load_module($1);
521        fail('load_module($1) from "Dies" succeeded!');
522    }
523    catch ($e) {
524        ok($e, 'load_module($1) from "Dies" fails');
525        like($e, qr/^Can't locate Dies[.]pm in \@INC/, 'load_module($1) from "Dies" fails with correct error');
526    }
527
528    # Loading module goes well
529    my $ret;
530    try {
531        $ret = load_module("strict");
532        pass('load_module("strict") worked');
533        is($ret, "strict", 'load_module("strict") returned "strict"');
534    }
535    catch ($e) {
536        fail('load_module("strict") errored: ' . $e);
537    }
538    $module_name = 'strict';
539    try {
540        $ret = load_module($module_name);
541        pass('load_module($module_name) $module_name=strict worked');
542        is($ret, "strict", 'load_module($module_name) returned "strict"');
543    }
544    catch ($e) {
545        fail('load_module($module_name) $module_name=strict errored: ' . $e);
546    }
547    $module_name =~ m!(\w+)!;
548    try {
549        $ret = load_module($1);
550        pass('load_module($1) from $module_name=strict worked');
551        is($ret, "strict", 'load_module($1) from $module_name=strict returned "strict"');
552    }
553    catch ($e) {
554        fail('load_module($1) from $module_name=strict errored: ' . $e);
555    }
556    "strict" =~ m!(\w+)!;
557    try {
558        $ret = load_module($1);
559        pass('load_module($1) from "strict" worked');
560        is($ret, "strict", 'load_module($1) from "strict" returned "strict"');
561    }
562    catch ($e) {
563        fail('load_module($1) from "strict" errored: ' . $e);
564    }
565
566    # Slightly more complex, based on tie
567    {
568        package BuiltinTestTie {
569            sub TIESCALAR {
570                bless $_[1], $_[0];
571            }
572            sub FETCH {
573                ${$_[0]}
574            }
575        }
576        my $x;
577        tie my $y, BuiltinTestTie => \$x;
578        $x = "strict";
579        try {
580            $ret = load_module($y);
581            pass('load_module($y) from $y tied to $x=strict worked');
582            is($ret, "strict", 'load_module($y) from $y tied to $x=strict worked and returned "strict"');
583        }
584        catch ($e) {
585            fail('load_module($y) from $y tied to $x=strict failed: ' . $e);
586        };
587    }
588
589    # Can be used to import a symbol to the current namespace, too:
590    {
591        my $aref = [];
592        my $aref_stringified = "$aref";
593        my $got = eval '
594            BEGIN {
595                load_module("builtin")->import("stringify");
596            }
597            stringify($aref);
598        ';
599        if (my $error = $@) {
600            fail('load_module("builtin")->import("stringify") failed: ' . $error);
601        }
602        is($got, $aref_stringified, 'load_module("builtin")->import("stringify") works, stringifying $aref');
603    }
604}
605
606# version bundles
607{
608    use builtin ':5.39';
609    ok(true, 'true() is available from :5.39 bundle');
610
611    # parse errors
612    foreach my $bundle (qw( :x :5.x :5.36x :5.36.1000 :5.1000 :5.36.1.2 ),
613                        ":  +5.+39", ":  +5.+40. -10", ": 5.40", ":5 .40", ":5.+40",
614                        ":5.40 .0", ":5.40.-10", ":5.40\0") {
615        (my $pretty_bundle = $bundle) =~ s/([^[:print:]])/ sprintf("\\%o", ord $1) /ge;
616        ok(!defined eval "use builtin '$bundle';", $pretty_bundle.' is invalid bundle');
617        like($@, qr/^Invalid version bundle "\Q$pretty_bundle\E" at /);
618    }
619}
620
621# github #21981
622{
623    fresh_perl_is(<<'EOS', "", {}, "github 21981: panic in intro_my");
624use B;
625BEGIN { B::save_BEGINs; }
626use v5.39;
627EOS
628}
629
630# github #22542
631{
632    # some of these functions don't error at this point, but they might be updated
633    # and see the same problem we fix here
634    for my $func (qw(is_bool is_weak blessed refaddr reftype ceil floor is_tainted
635                     trim stringify created_as_string created_as_number)) {
636        my $arg =
637          $func =~ /ceil|floor|created_as/ ? "1.1" :
638          $func =~ /(^ref|blessed|is_weak)/ ? "\\1" : '"abc"';
639        fresh_perl_is(<<"EOS", "ok", {}, "goto $func");
640no warnings "experimental";
641sub f { goto &builtin::$func }
642f($arg);
643print "ok";
644EOS
645    }
646}
647
648# vim: tabstop=4 shiftwidth=4 expandtab autoindent softtabstop=4
649
650done_testing();
651