Skip to content

Commit 7e96be5

Browse files
committed
mark CORE::__CLASS__ as non-ampable
Previously: CORE::__CLASS__ and compile-time aliasing (as in `BEGIN { *cls = \&CORE::__CLASS__; } ... cls`) worked as expected, but runtime calls like `&CORE::__CLASS__()` and `my $ref = \&CORE::__CLASS__; ... $ref->()` would produce bizarre results, behaving like CORE::__FILE__ instead. Now the latter throw a "&CORE::__CLASS__ cannot be called directly" error. This is perhaps not entirely satisfactory, but __CLASS__ is a bit special (it is not a true constant and only usable in methods) and erroring cleanly is better than silently returning wrong results. Fixes #23737. Assorted changes: - Document core_prototype()'s `opnum` parameter. - Add comment explaining what `*opnum = 0` means (and why it makes no sense for KEY___CLASS__). - Don't hardcode assumptions about keyword codes in coresub_op(). Handle __PACKAGE__/__FILE__/__LINE__ explicitly and assert() nothing else is passed in. This effectively reverts commit c2f605d. - Remove CORE::__CLASS__ from op/coreamp.t as it is no longer "ampable". - Extend op/coresubs.t to handle more of the idiosyncrasies of CORE::__CLASS__.
1 parent 6ddb6d7 commit 7e96be5

File tree

4 files changed

+52
-21
lines changed

4 files changed

+52
-21
lines changed

gv.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,7 @@ S_maybe_add_coresub(pTHX_ HV * const stash, GV *gv,
638638
case KEY_until: case KEY_use : case KEY_when : case KEY_while :
639639
case KEY_x : case KEY_xor : case KEY_y :
640640
return NULL;
641+
case KEY___CLASS__:
641642
case KEY_chomp: case KEY_chop: case KEY_defined: case KEY_delete:
642643
case KEY_eof : case KEY_exec: case KEY_exists :
643644
case KEY_lstat:

op.c

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16067,7 +16067,9 @@ Perl_custom_op_register(pTHX_ Perl_ppaddr_t ppaddr, const XOP *xop)
1606716067
This function assigns the prototype of the named core function to C<sv>, or
1606816068
to a new mortal SV if C<sv> is C<NULL>. It returns the modified C<sv>, or
1606916069
C<NULL> if the core function has no prototype. C<code> is a code as returned
16070-
by C<keyword()>. It must not be equal to 0.
16070+
by C<keyword()>. It must not be equal to 0. C<opnum> should be either C<NULL>
16071+
or the address of a variable that will be set to the op number corresponding to
16072+
C<name>, if any.
1607116073

1607216074
=cut
1607316075
*/
@@ -16106,13 +16108,14 @@ Perl_core_prototype(pTHX_ SV *sv, const char *name, const int code,
1610616108
case KEY_values: retsetpvs("\\[%@]", OP_VALUES);
1610716109
case KEY_each: retsetpvs("\\[%@]", OP_EACH);
1610816110
case KEY_pos: retsetpvs(";\\[$*]", OP_POS);
16109-
case KEY___CLASS__:
1611016111
case KEY___FILE__: case KEY___LINE__: case KEY___PACKAGE__:
16112+
/* special case:
16113+
0 means "no actual op, but can be emulated using caller()"
16114+
*/
1611116115
retsetpvs("", 0);
16112-
case KEY_evalbytes:
16113-
name = "entereval"; break;
16114-
case KEY_readpipe:
16115-
name = "backtick";
16116+
case KEY_evalbytes: name = "entereval"; break;
16117+
case KEY_readpipe: name = "backtick"; break;
16118+
case KEY___CLASS__: name = "classname"; break;
1611616119
}
1611716120

1611816121
#undef retsetpvs
@@ -16179,14 +16182,26 @@ Perl_coresub_op(pTHX_ SV * const coreargssv, const int code,
1617916182
PERL_ARGS_ASSERT_CORESUB_OP;
1618016183

1618116184
switch(opnum) {
16182-
case 0:
16183-
return op_append_elem(OP_LINESEQ,
16184-
argop,
16185-
newSLICEOP(0,
16186-
newSVOP(OP_CONST, 0, newSViv(-code % 3)),
16187-
newOP(OP_CALLER,0)
16188-
)
16189-
);
16185+
case 0: {
16186+
IV caller_index = IV_MAX;
16187+
switch (-code) {
16188+
case KEY___PACKAGE__: caller_index = 0; break;
16189+
case KEY___FILE__: caller_index = 1; break;
16190+
case KEY___LINE__: caller_index = 2; break;
16191+
}
16192+
assert(caller_index < IV_MAX);
16193+
16194+
return op_append_elem(
16195+
OP_LINESEQ,
16196+
argop,
16197+
newSLICEOP(
16198+
0,
16199+
newSVOP(OP_CONST, 0, newSViv(caller_index)),
16200+
newOP(OP_CALLER, 0)
16201+
)
16202+
);
16203+
}
16204+
1619016205
case OP_EACH:
1619116206
case OP_KEYS:
1619216207
case OP_VALUES:

t/op/coreamp.t

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,6 @@ undef *_;
303303
$tests++;
304304
pass('no crash with &CORE::foo when *_{ARRAY} is undef');
305305

306-
test_proto '__CLASS__';
307306
test_proto '__FILE__';
308307
test_proto '__LINE__';
309308
test_proto '__PACKAGE__';

t/op/coresubs.t

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,18 @@ while(<$kh>) {
7373

7474
inlinable_ok($word, $args_for{$word} || join ",", map "\$$_", 1..$numargs);
7575

76-
next if $word eq "__CLASS__";
77-
7876
# High-precedence tests
7977
my $hpcode;
8078
if (!$proto && defined $proto) { # nullary
81-
$hpcode = "sub { () = my$word + 1 }";
79+
if ($word eq '__CLASS__') {
80+
$hpcode = <<~_EOT_;
81+
use feature 'class';
82+
no warnings 'experimental::class';
83+
class TmpClassA { method { () = ::my$word + 1 } }
84+
_EOT_
85+
} else {
86+
$hpcode = "sub { () = my$word + 1 }";
87+
}
8288
}
8389
elsif ($proto =~ /^;?$protochar\z/) { # unary
8490
$hpcode = "sub { () = my$word "
@@ -91,7 +97,7 @@ while(<$kh>) {
9197
# ‘(eval 21)’ vs ‘(eval 22)’.
9298
no warnings 'numeric';
9399
my $core = op_list(eval $hpcode =~ s/my/CORE::/r or die);
94-
my $my = op_list(eval $hpcode or die);
100+
my $my = op_list(eval $hpcode =~ s/TmpClassA/TmpClassB/r or die);
95101
is $my, $core, "precedence of CORE::$word without parens";
96102
}
97103

@@ -104,7 +110,7 @@ while(<$kh>) {
104110

105111
$tests ++;
106112
my $code =
107-
"sub { () = (my$word("
113+
"() = (my$word("
108114
. (
109115
$args_for{$word}
110116
? $args_for{$word}.',$7'
@@ -114,7 +120,17 @@ while(<$kh>) {
114120
: 0
115121
)
116122
)
117-
. "))}";
123+
. "))";
124+
if ($word eq '__CLASS__') {
125+
$code =~ s/\b(my$word)\b/::$1/g;
126+
$code = <<~_EOT_;
127+
use feature 'class';
128+
no warnings 'experimental::class';
129+
class TmpClassX { method { $code } }
130+
_EOT_
131+
} else {
132+
$code = "sub { $code }";
133+
}
118134
eval $code;
119135
my $desc = $desc{$word} || $word;
120136
like $@, qr/^Too many arguments for $desc/,

0 commit comments

Comments
 (0)