Skip to content

Commit 09c58a1

Browse files
committed
Revert SKIP-only restriction - support all labeled blocks
All labeled blocks (SKIP, TODO, CLEANUP, etc.) should support non-local control flow with last/next/redo. The registry check now applies to all labeled blocks, not just SKIP. Testing shows: - skip_control_flow.t: all 3 tests pass ✓ - TODO labeled blocks: control flow works ✓ - uni/variables.t: runs to completion ✓
1 parent 4f02e2d commit 09c58a1

5 files changed

Lines changed: 1582 additions & 3 deletions

File tree

src/main/java/org/perlonjava/codegen/EmitBlock.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,9 @@ public static void emitBlock(EmitterVisitor emitterVisitor, BlockNode node) {
9999
element.accept(voidVisitor);
100100
}
101101

102-
// Check for non-local control flow after each statement in SKIP labeled blocks
102+
// Check for non-local control flow after each statement in labeled blocks
103103
// Only for simple blocks to avoid ASM VerifyError
104-
// Restricted to SKIP blocks to avoid interfering with other labeled blocks
105-
if (node.isLoop && "SKIP".equals(node.labelName) && i < list.size() - 1 && list.size() <= 3) {
104+
if (node.isLoop && node.labelName != null && i < list.size() - 1 && list.size() <= 3) {
106105
// Check if block contains loop constructs (they handle their own control flow)
107106
boolean hasLoopConstruct = false;
108107
for (Node elem : list) {

src/main/perl/lib/Test2/Handle.pm

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
package Test2::Handle;
2+
use strict;
3+
use warnings;
4+
5+
our $VERSION = '1.302219';
6+
7+
require Carp;
8+
require Test2::Util;
9+
10+
use Test2::Util::HashBase qw{
11+
+namespace
12+
+base
13+
+include
14+
+import
15+
+stomp
16+
};
17+
18+
my $NS = 1;
19+
20+
# Things we do not want to import automagically
21+
my %EXCLUDE_SYMBOLS = (
22+
BEGIN => 1,
23+
DESTROY => 1,
24+
DOES => 1,
25+
END => 1,
26+
VERSION => 1,
27+
does => 1,
28+
can => 1,
29+
isa => 1,
30+
import => 1,
31+
);
32+
33+
sub DEFAULT_HANDLE_BASE { Carp::croak("Not Implemented") }
34+
35+
sub HANDLE_BASE { $_[0]->{+BASE} }
36+
37+
sub HANDLE_NAMESPACE { $_[0]->{+NAMESPACE} }
38+
39+
sub _HANDLE_INCLUDE {
40+
my $self = shift;
41+
42+
return $self->{+IMPORT} if $self->{+IMPORT};
43+
44+
my $ns = $self->{+NAMESPACE};
45+
46+
my $line = __LINE__ + 3;
47+
$self->{+IMPORT} = eval <<" EOT" or die $@;
48+
#line $line ${ \__FILE__ }
49+
package $ns;
50+
sub {
51+
my (\$module, \$caller, \@imports) = \@_;
52+
unless (eval { require(Test2::Util::pkg_to_file(\$module)); 1 }) {
53+
my \$err = \$@;
54+
chomp(\$err);
55+
\$err =~ s/\.\$//;
56+
die "\$err (called from \$caller->[1] line \$caller->[2]).\n";
57+
}
58+
\$module->import(\@imports);
59+
};
60+
EOT
61+
}
62+
63+
sub HANDLE_INCLUDE {
64+
my $self = shift;
65+
my ($mod, @imports) = @_;
66+
@imports = @{$imports[0]} if @imports == 1 && ref($imports[0]) eq 'ARRAY';
67+
68+
my $caller = [caller];
69+
70+
$self->_HANDLE_INCLUDE->($mod, $caller, @imports);
71+
$self->_HANDLE_WRAP($_) for @imports;
72+
}
73+
74+
sub HANDLE_SUBS {
75+
my $self = shift;
76+
77+
my @out;
78+
79+
my $seen = {class => {}, export => {}};
80+
my @todo = ($self->{+NAMESPACE});
81+
82+
while (my $check = shift @todo) {
83+
next if $seen->{class}->{$check}++;
84+
85+
no strict 'refs';
86+
my $stash = \%{"$check\::"};
87+
push @out => grep { !$seen->{export}->{$_}++ && !$EXCLUDE_SYMBOLS{$_} && $_ !~ m/^_/ && $check->can($_) } keys %$stash;
88+
push @todo => @{"$check\::ISA"};
89+
}
90+
91+
return @out;
92+
}
93+
94+
sub _HANDLE_WRAP {
95+
my $self = shift;
96+
my ($name) = @_;
97+
98+
return if $self->SUPER::can($name);
99+
100+
my $wrap = sub {
101+
my $handle = shift;
102+
my $ns = $handle->{+NAMESPACE};
103+
my @caller = caller;
104+
my $sub = $ns->can($name) or die qq{"$name" is not provided by this T2 handle at $caller[1] line $caller[2].\n};
105+
goto &$sub;
106+
};
107+
108+
{
109+
no strict 'refs';
110+
*$name = $wrap;
111+
}
112+
113+
return $wrap;
114+
}
115+
116+
sub import {
117+
my $class = shift;
118+
my ($name, %params) = @_;
119+
120+
my $self = $class->new(%params);
121+
122+
my $caller = caller;
123+
no strict 'refs';
124+
*{"$caller\::$name"} = sub() { $self };
125+
}
126+
127+
sub init {
128+
my $self = shift;
129+
130+
my $stomp = $self->{+STOMP} ||= 0;
131+
my $inc = $self->{+INCLUDE} ||= [];
132+
my $base = $self->{+BASE} ||= $self->DEFAULT_HANDLE_BASE;
133+
134+
require(Test2::Util::pkg_to_file($base));
135+
136+
my $new;
137+
my $ns = $self->{+NAMESPACE} ||= do { $new = 1; __PACKAGE__ . '::GEN_' . $NS++ };
138+
139+
my $stash = do { no strict 'refs'; \%{"$ns\::"} };
140+
141+
Carp::croak("Namespace '$ns' already appears to be populated") if !$stomp && keys %$stash;
142+
143+
$INC{Test2::Util::pkg_to_file($ns)} ||= __FILE__ if $new;
144+
145+
{
146+
no strict 'refs';
147+
push @{"$ns\::ISA"} => $self->{+BASE};
148+
}
149+
150+
if (my $include = $self->{+INCLUDE}) {
151+
my $r = ref($include);
152+
if ($r eq 'ARRAY') {
153+
$self->HANDLE_INCLUDE(ref($_) ? @{$_} : $_) for @$include;
154+
}
155+
elsif ($r eq 'HASH') {
156+
$self->HANDLE_INCLUDE($_ => $include->{$_}) for keys %$include;
157+
}
158+
else {
159+
die "Not sure what to do with '$r'";
160+
}
161+
}
162+
}
163+
164+
sub can {
165+
my $self = shift;
166+
my ($name) = @_;
167+
168+
my $sub = $self->SUPER::can($name);
169+
return $sub if $sub;
170+
171+
return undef unless ref $self;
172+
173+
$self->{+NAMESPACE}->can($name) or return undef;
174+
return $self->_HANDLE_WRAP($name);
175+
}
176+
177+
sub AUTOLOAD {
178+
my ($self) = @_;
179+
180+
my ($name) = (our $AUTOLOAD =~ m/^(?:.*::)?([^:]+)$/);
181+
return if $EXCLUDE_SYMBOLS{$name};
182+
183+
my $wrap = $self->_HANDLE_WRAP($name);
184+
goto &$wrap;
185+
}
186+
187+
1;
188+
189+
__END__
190+
191+
=pod
192+
193+
=encoding UTF-8
194+
195+
=head1 NAME
196+
197+
Test2::Handle - Base class for Test2 handles used in V# bundles.
198+
199+
=head1 DESCRIPTION
200+
201+
This is what you interact with when you use the C<T2()> function in a test that
202+
uses L<Test2::V1>.
203+
204+
=head1 SYNOPSIS
205+
206+
=head2 RECOMMENDED
207+
208+
use Test2::V1;
209+
210+
my $handle = T2();
211+
212+
$handle->ok(1, "Passing Test");
213+
214+
=head2 WITHOUT SUGAR
215+
216+
use Test2::Handle();
217+
218+
my $handle = Test2::Handle->new(base => 'Test2::V1::Base');
219+
220+
$handle->ok(1, "Passing test");
221+
222+
=head1 METHODS
223+
224+
Most methods are delegated to the base class provided at construction. There
225+
are however a few methods that are defined by this package itself.
226+
227+
=over 4
228+
229+
=item $base = $class_or_inst->DEFAULT_HANDLE_BASE
230+
231+
Get the default handle base. This throws an exception on the base handle class,
232+
you should override it in a subclass.
233+
234+
=item $base = $inst->HANDLE_BASE
235+
236+
In this base class this method always throws an exception. In a subclass it
237+
should return the default base class to use for that subclass.
238+
239+
=item $namespace = $inst->HANDLE_NAMESPACE
240+
241+
Get the namespace used to store function we wrap as methods.
242+
243+
=item @sub_names = $inst->HANDLE_SUBS
244+
245+
Get a list of all subs available in the handle namespace.
246+
247+
=item $inst->HANDLE_INCLUDE($package, @subs)
248+
249+
Import the specified subs from the specified package into our internal
250+
namespace.
251+
252+
=item $inst = $class->import()
253+
254+
Used to create a C<T2()> sub in your namsepace at import.
255+
256+
=item $inst->init()
257+
258+
Internally used to intialize and validate the handle object.
259+
260+
=item AUTOLOAD
261+
262+
Internally used to wrap functions as methods.
263+
264+
=back
265+
266+
=head1 SOURCE
267+
268+
The source code repository for Test2-Suite can be found at
269+
F<https://github.com/Test-More/test-more/>.
270+
271+
=head1 MAINTAINERS
272+
273+
=over 4
274+
275+
=item Chad Granum E<lt>exodist@cpan.orgE<gt>
276+
277+
=back
278+
279+
=head1 AUTHORS
280+
281+
=over 4
282+
283+
=item Chad Granum E<lt>exodist@cpan.orgE<gt>
284+
285+
=back
286+
287+
=head1 COPYRIGHT
288+
289+
Copyright Chad Granum E<lt>exodist@cpan.orgE<gt>.
290+
291+
This program is free software; you can redistribute it and/or
292+
modify it under the same terms as Perl itself.
293+
294+
See F<http://dev.perl.org/licenses/>
295+
296+
=cut

0 commit comments

Comments
 (0)