Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public static RuntimeList map(RuntimeList runtimeList, RuntimeScalar perlMapClos
List<RuntimeBase> transformedElements = new ArrayList<>();

RuntimeScalar saveValue = getGlobalVariable("main::_");
// Map results are captured by the caller after the operator returns;
// flushing between iterations can destroy blessed return values early.
boolean wasFlushing = MortalList.suppressFlush(true);

try {
// Use the outer @_ instead of an empty array
Expand Down Expand Up @@ -87,6 +90,7 @@ public static RuntimeList map(RuntimeList runtimeList, RuntimeScalar perlMapClos
return transformedList;
}
} finally {
MortalList.suppressFlush(wasFlushing);
GlobalVariable.aliasGlobalVariable("main::_", saveValue);
releaseEphemeralCaptures(perlMapClosure);
}
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/org/perlonjava/runtime/perlmodule/Subs.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public Subs() {
public static void initialize() {
Subs subs = new Subs();
try {
GlobalVariable.getGlobalVariable("subs::VERSION").set(new RuntimeScalar("1.04"));
subs.registerMethod("import", "importSubs", ";$");
subs.registerMethod("mark_overridable", "markOverridable", "$$");
} catch (NoSuchMethodException e) {
Expand All @@ -45,7 +46,10 @@ public static RuntimeList importSubs(RuntimeArray args, int ctx) {
for (RuntimeScalar variableObj : args.elements) {
String variableString = variableObj.toString();
String fullName = caller + "::" + variableString;
GlobalVariable.getGlobalCodeRef(fullName);
RuntimeScalar codeRef = GlobalVariable.getGlobalCodeRef(fullName);
if (codeRef.value instanceof RuntimeCode code) {
code.isDeclared = true;
}
GlobalVariable.isSubs.put(fullName, true);
}

Expand Down
37 changes: 37 additions & 0 deletions src/main/perl/lib/subs.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package subs;

use strict;
use warnings;

our $VERSION = '1.04';

sub import {
my $callpack = caller;
shift;

for my $sym (@_) {
no strict 'refs';
*{"${callpack}::$sym"} = \&{"${callpack}::$sym"};
}
}

1;

__END__

=head1 NAME

subs - Perl pragma to predeclare subroutine names

=head1 SYNOPSIS

use subs qw(foo bar);

=head1 DESCRIPTION

Predeclares package subroutine names by creating CODE slots in the caller's
stash. This matches the core pragma behavior used by modules that want to
install generated methods only when a custom method has not already been
declared.

=cut
25 changes: 25 additions & 0 deletions src/test/resources/unit/refcount/map_return_destroy.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use strict;
use warnings;
use Test::More;

{
package MapReturnDestroy;
our $counter = 0;
sub new {
my $class = shift;
my $self = bless {}, $class;
$counter++;
return $self;
}
sub DESTROY {
$counter-- if $counter > 0;
}
}

my @objs = map { MapReturnDestroy->new } 1 .. 3;
is($MapReturnDestroy::counter, 3, "map-returned objects survive until caller captures list");

@objs = ();
is($MapReturnDestroy::counter, 0, "map-returned objects are destroyed when array is cleared");

done_testing;
31 changes: 31 additions & 0 deletions src/test/resources/unit/subs_pragma.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use strict;
use warnings;
use Test::More;

our @warnings;
BEGIN { $SIG{__WARN__} = sub { push @main::warnings, @_ } }

{
package SubsPragmaCustom;
use strict;
use warnings;

use subs qw(foo);

BEGIN {
no strict 'refs';
*{"SubsPragmaCustom::foo"} = sub { "generated" }
unless *{"SubsPragmaCustom::foo"}{CODE};
}

sub foo { "custom" }
}

is(SubsPragmaCustom::foo(), "custom", "predeclared sub can be custom-defined later");
{
no strict 'refs';
ok(*{"SubsPragmaCustom::foo"}{CODE}, "predeclared sub has a visible CODE slot");
}
is_deeply(\@warnings, [], "predeclared custom sub does not warn as redefined");

done_testing;
Loading