#!perl use strict; use warnings; use Runops::Trace; use Test::More 'no_plan'; use Scalar::Util qw(refaddr); my ( $called, @ops, @refgen_args, @aassign_args ); Runops::Trace::set_tracer(sub { my ( $op, $arity, @args ) = @_; $called++; if ( $op->name eq 'refgen' and @refgen_args < 2 ) { push @refgen_args, [ @args ]; } elsif ( $op->name eq 'aassign' ) { push @aassign_args, [ @args ]; } push @ops, $op; }); my $i; ++$i; sub foo { sub { $i } }; sub bar { sub { $i } }; Runops::Trace::enable_tracing(); ++$i; my $j = $i + 42; my $y = 101; my ( $x, @refs ) = \( $y, [qw/dancing hippies/], 33, \&foo ); $i ? foo() : bar(); if ( foo() || 1 ) { $j = "" . $i; } Runops::Trace::disable_tracing(); ++$i; is( $i, 3, "ops dispatched" ); ok( $called, "hook called" ); ok( scalar(@ops), "cought some ops" ); my %seen_names; foreach my $op ( @ops ) { isa_ok( $op, "B::OP" ); $seen_names{$op->name}++; } foreach my $opname (qw( nextstate preinc add entersub leavesub refgen sassign aassign padsv padav gv cond_expr and or const anonlist concat )) { ok( $seen_names{$opname}, "$opname op seen by hook" ); } is_deeply( \@refgen_args, [ [ \&foo ], [ $x, @refs ] ], "listop arg capture" ); is( refaddr($refgen_args[1][0]), refaddr($x), "aliasing semantics" ); is_deeply( \@aassign_args, [[ [ \$x, \@refs ], # two lvalues [ \$x, \(@refs) ], # four rvalues, passed to the hook by ref ]], "aassign", );