diff --git a/Parser.pm b/Parser.pm index c938e8c..8944553 100644 --- a/Parser.pm +++ b/Parser.pm @@ -146,8 +146,13 @@ sub parse_start { my $expatnb = XML::Parser::ExpatNB->new( @expat_options, @_ ); $expatnb->setHandlers(%handlers); - &$init($expatnb) - if defined($init); + if (defined($init)) { + eval { &$init($expatnb) }; + if ($@) { + $expatnb->release; + die $@; + } + } $expatnb->{_State_} = 1; @@ -177,8 +182,13 @@ sub parse { $expat->base( $self->{Base} ); } - &$init($expat) - if defined($init); + if (defined($init)) { + eval { &$init($expat) }; + if ($@) { + $expat->release; + die $@; + } + } my @result = (); my $result; @@ -190,16 +200,22 @@ sub parse { } if ( $result and defined($final) ) { - if (wantarray) { - @result = &$final($expat); - } - else { - $result = &$final($expat); - } + my $want = wantarray; + eval { + if ($want) { + @result = &$final($expat); + } + else { + $result = &$final($expat); + } + }; + $err = $@; } $expat->release; + die $err if $err; + return unless defined wantarray; return wantarray ? @result : $result; } diff --git a/t/parser_api.t b/t/parser_api.t index d94fdfa..8e57fb1 100644 --- a/t/parser_api.t +++ b/t/parser_api.t @@ -213,4 +213,52 @@ my $simple_xml = 'text'; is($p->{Base}, 'saved_base', 'parsefile restores Base even after error'); } +# --- Init handler die releases parser (no circular ref leak) --- +{ + my $released = 0; + my $p = XML::Parser->new( + Handlers => { + Init => sub { die "init failed\n" }, + }, + ); + eval { $p->parse('') }; + like($@, qr/init failed/, 'Init handler die propagates correctly'); + # Parser should still be usable after Init failure + my $ok = eval { + $p->setHandlers(Init => undef); + $p->parse(''); + 1; + }; + ok($ok, 'Parser reusable after Init handler failure'); +} + +# --- Final handler die still releases parser --- +{ + my $p = XML::Parser->new( + Handlers => { + Final => sub { die "final failed\n" }, + }, + ); + eval { $p->parse('') }; + like($@, qr/final failed/, 'Final handler die propagates correctly'); + # Parser should still be usable + my $ok = eval { + $p->setHandlers(Final => undef); + $p->parse(''); + 1; + }; + ok($ok, 'Parser reusable after Final handler failure'); +} + +# --- parse_start Init handler die releases parser --- +{ + my $p = XML::Parser->new( + Handlers => { + Init => sub { die "init_nb failed\n" }, + }, + ); + eval { $p->parse_start() }; + like($@, qr/init_nb failed/, 'parse_start Init handler die propagates correctly'); +} + done_testing;