From 8769858513e70fa318195b6f3ef697656d53a07d Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Tue, 21 Apr 2026 11:55:41 +0200 Subject: [PATCH] fix(Filter::Util::Call): implement method filters and fix block-mode filter_read Two bugs in FilterUtilCall prevented source-filter modules that use the method-filter form (e.g. CPAN's Switch.pm) from working: 1. Method filters were unimplemented. applyFilters() had a TODO branch that returned the source unchanged whenever filter_add was called with a blessed ref instead of a coderef, so `use Switch` never rewrote any switch/case blocks and produced a bare syntax error. Now we look up ${pkg}::filter via InheritanceResolver and drive it the same way we drive closure filters: call it with $self as the first arg, accumulate $_, loop until status <= 0. 2. filter_read($blocksize) wrongly terminated block reads at the first "\n", turning block mode into line mode. Switch::filter asks for 1_000_000 bytes expecting the whole source, but only got one line per call, which made Text::Balanced::_match_codeblock fail with "Bad switch statement (problem in the code block?)". Removed the early newline break so block mode reads up to $blocksize bytes. Verified with `./jcpan -t Switch`: Files=3, Tests=590, Result: PASS (was 0/3 test files before, all failing with syntax errors). `make` still passes with no unit-test regressions. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../org/perlonjava/core/Configuration.java | 4 +- .../runtime/perlmodule/FilterUtilCall.java | 49 +++++++++++++++---- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/perlonjava/core/Configuration.java b/src/main/java/org/perlonjava/core/Configuration.java index 3b20f5ed9..4412a9eb9 100644 --- a/src/main/java/org/perlonjava/core/Configuration.java +++ b/src/main/java/org/perlonjava/core/Configuration.java @@ -33,7 +33,7 @@ public final class Configuration { * Automatically populated by Gradle/Maven during build. * DO NOT EDIT MANUALLY - this value is replaced at build time. */ - public static final String gitCommitId = "f8e0a8fc1"; + public static final String gitCommitId = "ef6eaab61"; /** * Git commit date of the build (ISO format: YYYY-MM-DD). @@ -48,7 +48,7 @@ public final class Configuration { * Parsed by App::perlbrew and other tools via: perl -V | grep "Compiled at" * DO NOT EDIT MANUALLY - this value is replaced at build time. */ - public static final String buildTimestamp = "Apr 21 2026 11:24:02"; + public static final String buildTimestamp = "Apr 21 2026 11:56:14"; // Prevent instantiation private Configuration() { diff --git a/src/main/java/org/perlonjava/runtime/perlmodule/FilterUtilCall.java b/src/main/java/org/perlonjava/runtime/perlmodule/FilterUtilCall.java index 4db2f5fb5..a21fc7b44 100644 --- a/src/main/java/org/perlonjava/runtime/perlmodule/FilterUtilCall.java +++ b/src/main/java/org/perlonjava/runtime/perlmodule/FilterUtilCall.java @@ -179,11 +179,8 @@ public static RuntimeList filter_read(RuntimeArray args, int ctx) { block.append(line); bytesRead += line.length(); context.currentLine++; - - // Check if line ends with newline to stop - if (line.endsWith("\n")) { - break; - } + // In block mode, keep reading until we hit blockSize or EOF. + // Do NOT stop on newlines — that's line mode. } else { // Partial line read int remaining = blockSize - bytesRead; @@ -332,11 +329,43 @@ public static String applyFilters(String sourceCode) { GlobalVariable.getGlobalVariable("main::_").set(""); } } else { - // Method filter: call the filter() method on the object - // String filterMethod = packageName.toString() + "::filter"; - // TODO: Implement method filter calling - // For now, just return the original source - return sourceCode; + // Method filter: call the "filter" method on the blessed filter object + // repeatedly until it returns a non-positive status. + String pkg = packageName.toString(); + RuntimeScalar method = org.perlonjava.runtime.mro.InheritanceResolver + .findMethodInHierarchy("filter", pkg, null, 0); + if (method == null || method.type != org.perlonjava.runtime.runtimetypes.RuntimeScalarType.CODE) { + if (debug) { + System.err.println("[FILTER] No filter() method found in " + pkg + + "; returning source unchanged"); + } + return sourceCode; + } + RuntimeCode code = (RuntimeCode) method.value; + boolean continueFiltering = true; + while (continueFiltering) { + // Call $obj->filter (pass $self as first arg) + RuntimeArray callArgs = new RuntimeArray(); + callArgs.push(filterObj); + RuntimeBase result = code.apply(callArgs, RuntimeContextType.SCALAR); + + String chunk = GlobalVariable.getGlobalVariable("main::_").toString(); + if (!chunk.isEmpty()) { + filteredCode.append(chunk); + if (debug) { + System.err.println("[FILTER] Method filter chunk: " + chunk); + } + } + + int status = result.scalar().getInt(); + if (debug) { + System.err.println("[FILTER] Method filter status: " + status); + } + if (status <= 0) { + continueFiltering = false; + } + GlobalVariable.getGlobalVariable("main::_").set(""); + } } }