Skip to content

Commit 8ac54f3

Browse files
committed
ParseXS: add tests for typeless param placeholders
In XS, you can declare a parameter without a type being specified (neither in the signature, nor in an INPUT line) and it informally becomes a placeholder: no C variable of that name is declared, but it causes the next arg to be skipped. For example, int foo(int A, B, int C) ... emits C code which: checks for 3 args, declares A and C as vars, and assigns them the values of ST(0) and ST(2). This commit adds some tests which confirm this behaviour. The semantics of such XSUBs with no body (i.e. autocall) are a bit odd. The parameter is passed as an arg to the wrapped C function even though the C var hasn't been declared or initialised. I decided to keep that as-is, because it's possible that the var has instead been declared in a PREINIT section. (I have no idea why you would want to do that, which makes it almost certain that some XS module on CPAN is in fact doing it.) Note that placeholders with default values are supported: both the var and the default value are ignored, and the arg is skipped. This is a bit ugly, but it allows an XSUB to accept a variable number of parameters, even if one of those parameters is no longer used. (And like most such things, I first discovered that it was a "thing" by seeing DB_File.xs break when I tried to disallow it. If there is a weird or impossible thing to do in XS, you can be certain that DB_File is doing it.)
1 parent aec2cdc commit 8ac54f3

File tree

1 file changed

+119
-2
lines changed

1 file changed

+119
-2
lines changed

dist/ExtUtils-ParseXS/t/001-basic.t

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/perl
22

33
use strict;
4-
use Test::More tests => 435;
4+
use Test::More tests => 460;
55
use Config;
66
use DynaLoader;
77
use ExtUtils::CBuilder;
@@ -1631,7 +1631,7 @@ EOF
16311631
],
16321632

16331633
[
1634-
# shady but legal
1634+
# shady but legal - placeholder
16351635
"auto-generated proto with no type",
16361636
[
16371637
'void',
@@ -2668,3 +2668,120 @@ EOF
26682668
26692669
test_many($preamble, 'XS_Foo_', \@test_fns);
26702670
}
2671+
2672+
{
2673+
# Test placeholders - various semi-official ways to to mark an
2674+
# argument as 'unused'.
2675+
2676+
my $preamble = Q(<<'EOF');
2677+
|MODULE = Foo PACKAGE = Foo
2678+
|
2679+
|PROTOTYPES: DISABLE
2680+
|
2681+
EOF
2682+
2683+
my @test_fns = (
2684+
2685+
[
2686+
"placeholder: typeless param with CODE",
2687+
[ Q(<<'EOF') ],
2688+
|int
2689+
|foo(int AAA, BBB, int CCC)
2690+
| CODE:
2691+
| XYZ;
2692+
EOF
2693+
[ 0, 0, qr/_usage\(cv,\s*"AAA, BBB, CCC"\)/, "usage" ],
2694+
[ 0, 0, qr/\bint\s+AAA\s*=\s*.*\Q(ST(0))/, "AAA is ST(0)" ],
2695+
[ 0, 0, qr/\bint\s+CCC\s*=\s*.*\Q(ST(2))/, "CCC is ST(2)" ],
2696+
[ 0, 1, qr/\bBBB;/, "no BBB decl" ],
2697+
],
2698+
2699+
[
2700+
"placeholder: typeless param bodiless",
2701+
[ Q(<<'EOF') ],
2702+
|int
2703+
|foo(int AAA, BBB, int CCC)
2704+
EOF
2705+
[ 0, 0, qr/_usage\(cv,\s*"AAA, BBB, CCC"\)/, "usage" ],
2706+
# Note that autocall uses the BBB var even though it isn't
2707+
# declared. It would be up to the coder to use C_ARGS, or add
2708+
# such a var via PREINIT.
2709+
[ 0, 0, qr/\bRETVAL\s*=\s*\Qfoo(AAA, BBB, CCC);/, "autocall" ],
2710+
[ 0, 0, qr/\bint\s+AAA\s*=\s*.*\Q(ST(0))/, "AAA is ST(0)" ],
2711+
[ 0, 0, qr/\bint\s+CCC\s*=\s*.*\Q(ST(2))/, "CCC is ST(2)" ],
2712+
[ 0, 1, qr/\bBBB;/, "no BBB decl" ],
2713+
],
2714+
2715+
[
2716+
# this is the only IN/OUT etc one which works, since IN is the
2717+
# default.
2718+
"placeholder: typeless IN param with CODE",
2719+
[ Q(<<'EOF') ],
2720+
|int
2721+
|foo(int AAA, IN BBB, int CCC)
2722+
| CODE:
2723+
| XYZ;
2724+
EOF
2725+
[ 0, 0, qr/_usage\(cv,\s*"AAA, BBB, CCC"\)/, "usage" ],
2726+
[ 0, 0, qr/\bint\s+AAA\s*=\s*.*\Q(ST(0))/, "AAA is ST(0)" ],
2727+
[ 0, 0, qr/\bint\s+CCC\s*=\s*.*\Q(ST(2))/, "CCC is ST(2)" ],
2728+
[ 0, 1, qr/\bBBB;/, "no BBB decl" ],
2729+
],
2730+
2731+
2732+
[
2733+
"placeholder: typeless OUT param with CODE",
2734+
[ Q(<<'EOF') ],
2735+
|int
2736+
|foo(int AAA, OUT BBB, int CCC)
2737+
| CODE:
2738+
| XYZ;
2739+
EOF
2740+
[ 1, 0, qr/Can't determine output type for 'BBB'/, "got type err" ],
2741+
],
2742+
2743+
[
2744+
"placeholder: typeless IN_OUT param with CODE",
2745+
[ Q(<<'EOF') ],
2746+
|int
2747+
|foo(int AAA, IN_OUT BBB, int CCC)
2748+
| CODE:
2749+
| XYZ;
2750+
EOF
2751+
[ 1, 0, qr/Can't determine output type for 'BBB'/, "got type err" ],
2752+
],
2753+
2754+
[
2755+
"placeholder: typeless OUTLIST param with CODE",
2756+
[ Q(<<'EOF') ],
2757+
|int
2758+
|foo(int AAA, OUTLIST BBB, int CCC)
2759+
| CODE:
2760+
| XYZ;
2761+
EOF
2762+
[ 1, 0, qr/Can't determine output type for 'BBB'/, "got type err" ],
2763+
],
2764+
2765+
[
2766+
# a placeholder with a default value may not seem to make much
2767+
# sense, but it allows an argument to still be passed (or
2768+
# not), even if it;s no longer used.
2769+
"placeholder: typeless default param with CODE",
2770+
[ Q(<<'EOF') ],
2771+
|int
2772+
|foo(int AAA, BBB = 888, int CCC = 999)
2773+
| CODE:
2774+
| XYZ;
2775+
EOF
2776+
[ 0, 0, qr/_usage\(cv,\s*"AAA, BBB = 888, CCC\s*= 999"\)/,"usage" ],
2777+
[ 0, 0, qr/\bint\s+AAA\s*=\s*.*\Q(ST(0))/, "AAA is ST(0)" ],
2778+
[ 0, 0, qr/\bCCC\s*=\s*.*\Q(ST(2))/, "CCC is ST(2)" ],
2779+
[ 0, 1, qr/\bBBB;/, "no BBB decl" ],
2780+
[ 0, 1, qr/\b888\s*;/, "no 888 usage" ],
2781+
],
2782+
2783+
2784+
);
2785+
2786+
test_many($preamble, 'XS_Foo_', \@test_fns);
2787+
}

0 commit comments

Comments
 (0)