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
42 changes: 25 additions & 17 deletions dev/tools/run_exiftool_tests.pl
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ sub harvest {
$result->{name} = $name;
$results{$name} = $result;

my %sym = (pass => "\x{2713}", fail => "\x{2717}", error => '!', timeout => 'T');
my %sym = (pass => "\x{2713}", fail => "\x{2717}", error => '!', timeout => 'T', incomplete => "\x{25CB}");
my $ch = $sym{$result->{status}} // '?';

printf "[%3d/%d] %-30s %s %d/%d ok (%.1fs)\n",
Expand Down Expand Up @@ -225,13 +225,15 @@ sub parse_tap {
} elsif ($total == 0 && $exit_code != 0) {
$status = 'error';
push @errors, "exit code $exit_code with no TAP output";
} elsif ($fail == 0 && $pass > 0) {
$status = 'pass';
} elsif ($planned > 0 && $total < $planned) {
# Check for incomplete tests BEFORE marking as pass
my $missing = $planned - $total;
$status = 'incomplete';
push @errors, "missing $missing of $planned tests";
} elsif ($fail > 0) {
$status = 'fail';
} elsif ($planned > 0 && $total < $planned) {
$status = 'error';
push @errors, "planned $planned but ran $total";
} elsif ($fail == 0 && $pass > 0) {
$status = 'pass';
} else {
$status = $exit_code == 0 ? 'pass' : 'error';
}
Expand All @@ -247,41 +249,47 @@ sub parse_tap {
}

sub print_summary {
my (%s, $total_pass, $total_fail, $total_planned);
$total_pass = $total_fail = $total_planned = 0;
my (%s, $total_pass, $total_fail, $total_planned, $total_missing);
$total_pass = $total_fail = $total_planned = $total_missing = 0;
for my $r (values %results) {
$s{$r->{status}}++;
$total_pass += $r->{pass} // 0;
$total_fail += $r->{fail} // 0;
$total_planned += $r->{planned} // 0;
my $ran = ($r->{pass} // 0) + ($r->{fail} // 0);
my $plan = $r->{planned} // 0;
$total_missing += ($plan - $ran) if $plan > $ran;
}

print "\nEXIFTOOL TEST SUMMARY:\n";
printf " Test files: %d\n", scalar keys %results;
printf " Passed: %d\n", $s{pass} // 0;
printf " Failed: %d\n", $s{fail} // 0;
printf " Errors: %d\n", $s{error} // 0;
printf " Timeouts: %d\n", $s{timeout} // 0;
printf " Passed: %d\n", $s{pass} // 0;
printf " Failed: %d\n", $s{fail} // 0;
printf " Incomplete: %d\n", $s{incomplete} // 0;
printf " Errors: %d\n", $s{error} // 0;
printf " Timeouts: %d\n", $s{timeout} // 0;
print "\n";
printf " Planned: %d\n", $total_planned;
printf " Total tests: %d\n", $total_pass + $total_fail;
printf " OK: %d\n", $total_pass;
printf " Not OK: %d\n", $total_fail;
printf " Missing: %d\n", $total_missing if $total_missing > 0;

if ($total_pass + $total_fail > 0) {
printf " Pass rate: %.1f%%\n", $total_pass * 100 / ($total_pass + $total_fail);
if ($total_planned > 0) {
printf " Pass rate: %.1f%% (of planned)\n", $total_pass * 100 / $total_planned;
}

my @failures = sort grep { $results{$_}{status} ne 'pass' } keys %results;
if (@failures) {
print "\nFAILED/ERROR TESTS:\n";
print "\nFAILED/ERROR/INCOMPLETE TESTS:\n";
for my $name (@failures) {
my $r = $results{$name};
printf " %-30s %s", $name, $r->{status};
printf " %-30s %-10s", $name, $r->{status};
printf " (%d/%d ok)", $r->{pass}, $r->{planned} || ($r->{pass} + $r->{fail})
if $r->{pass} || $r->{fail};
if ($r->{error}) {
my $err = $r->{error};
$err = substr($err, 0, 60) . "..." if length($err) > 60;
$err = substr($err, 0, 50) . "..." if length($err) > 50;
print " $err";
}
print "\n";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -708,15 +708,17 @@ public static String disassemble(InterpretedCode interpretedCode) {
break;
case Opcodes.POST_AUTOINCREMENT:
rd = interpretedCode.bytecode[pc++];
sb.append("POST_AUTOINCREMENT r").append(rd).append("++\n");
int postIncSrc = interpretedCode.bytecode[pc++];
sb.append("POST_AUTOINCREMENT r").append(rd).append(" = r").append(postIncSrc).append("++\n");
break;
case Opcodes.PRE_AUTODECREMENT:
rd = interpretedCode.bytecode[pc++];
sb.append("PRE_AUTODECREMENT --r").append(rd).append("\n");
break;
case Opcodes.POST_AUTODECREMENT:
rd = interpretedCode.bytecode[pc++];
sb.append("POST_AUTODECREMENT r").append(rd).append("--\n");
int postDecSrc = interpretedCode.bytecode[pc++];
sb.append("POST_AUTODECREMENT r").append(rd).append(" = r").append(postDecSrc).append("--\n");
break;
case Opcodes.PRINT: {
int contentReg = interpretedCode.bytecode[pc++];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,20 +433,41 @@ public static int executeArraySet(int[] bytecode, int pc, RuntimeBase[] register
public static int executeArrayPush(int[] bytecode, int pc, RuntimeBase[] registers) {
int arrayReg = bytecode[pc++];
int valueReg = bytecode[pc++];
RuntimeArray arr = (RuntimeArray) registers[arrayReg];
RuntimeArray arr = getArrayFromRegister(registers, arrayReg);
RuntimeBase val = registers[valueReg];
arr.push(val);
return pc;
}

/**
* Helper to get RuntimeArray from a register, handling RuntimeList conversion.
*/
private static RuntimeArray getArrayFromRegister(RuntimeBase[] registers, int arrayReg) {
RuntimeBase arrayBase = registers[arrayReg];
if (arrayBase instanceof RuntimeArray) {
return (RuntimeArray) arrayBase;
} else if (arrayBase instanceof RuntimeList) {
// Convert RuntimeList to RuntimeArray (defensive handling)
RuntimeArray arr = new RuntimeArray();
arrayBase.addToArray(arr);
registers[arrayReg] = arr;
return arr;
} else {
// Fallback: try to get as array via dereference
RuntimeArray arr = arrayBase.scalar().arrayDeref();
registers[arrayReg] = arr;
return arr;
}
}

/**
* Array pop: rd = pop(@array)
* Format: ARRAY_POP rd arrayReg
*/
public static int executeArrayPop(int[] bytecode, int pc, RuntimeBase[] registers) {
int rd = bytecode[pc++];
int arrayReg = bytecode[pc++];
RuntimeArray arr = (RuntimeArray) registers[arrayReg];
RuntimeArray arr = getArrayFromRegister(registers, arrayReg);
registers[rd] = RuntimeArray.pop(arr);
return pc;
}
Expand All @@ -458,7 +479,7 @@ public static int executeArrayPop(int[] bytecode, int pc, RuntimeBase[] register
public static int executeArrayShift(int[] bytecode, int pc, RuntimeBase[] registers) {
int rd = bytecode[pc++];
int arrayReg = bytecode[pc++];
RuntimeArray arr = (RuntimeArray) registers[arrayReg];
RuntimeArray arr = getArrayFromRegister(registers, arrayReg);
registers[rd] = RuntimeArray.shift(arr);
return pc;
}
Expand All @@ -470,7 +491,7 @@ public static int executeArrayShift(int[] bytecode, int pc, RuntimeBase[] regist
public static int executeArrayUnshift(int[] bytecode, int pc, RuntimeBase[] registers) {
int arrayReg = bytecode[pc++];
int valueReg = bytecode[pc++];
RuntimeArray arr = (RuntimeArray) registers[arrayReg];
RuntimeArray arr = getArrayFromRegister(registers, arrayReg);
RuntimeBase val = registers[valueReg];
RuntimeArray.unshift(arr, val);
return pc;
Expand Down
14 changes: 3 additions & 11 deletions src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,9 @@ public class InterpretedCode extends RuntimeCode implements PerlSubroutine {
* otherwise allocates a new one (recursive call).
*/
public RuntimeBase[] getRegisters() {
if (registersInUse.get()) {
// Recursive call - need fresh array
return new RuntimeBase[maxRegisters];
}
RuntimeBase[] regs = cachedRegisters.get();
if (regs == null || regs.length != maxRegisters) {
regs = new RuntimeBase[maxRegisters];
cachedRegisters.set(regs);
}
registersInUse.set(true);
return regs;
// Disable register pooling for now - it causes subtle bugs with stale values
// TODO: Investigate why Arrays.fill(regs, null) doesn't fully fix the issue
return new RuntimeBase[maxRegisters];
}

/**
Expand Down
Loading
Loading