Skip to content

fix(encode,errno): add iso-10646-1 alias and make local $! reset errno#616

Merged
fglock merged 2 commits intomasterfrom
fix/encode-iso10646-and-local-errno
Apr 29, 2026
Merged

fix(encode,errno): add iso-10646-1 alias and make local $! reset errno#616
fglock merged 2 commits intomasterfrom
fix/encode-iso10646-and-local-errno

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Apr 29, 2026

Summary

Investigation of jcpan -t CSV::Processor revealed two related PerlOnJava bugs that cascaded through the dependency chain (CSV::Processor → Text::AutoCSV → File::BOM). Both are fixed here in a single commit.

1. Encode.java: missing iso-10646-1 alias

Real Perl's Encode module defines iso-10646-1 as an alias for UCS-2. PerlOnJava had aliases for UCS-2, UCS-2BE, UCS-2LE but not iso-10646-1 / ISO-10646-1.

File::BOM references this alias when populating its enc2bom table, so use File::BOM died at compile time with Unknown encoding: iso-10646-1. The cascading effect was that everything depending on File::BOM (Text::AutoCSV, etc.) also failed to load.

2. ErrnoVariable.dynamicSaveState: local $! was a no-op

local $!; is supposed to clear $! to the empty/no-error state inside the dynamic scope (and restore the prior value on scope exit). The base RuntimeScalar.dynamicSaveState resets type/value to UNDEF, but ErrnoVariable overrides the readers (toString, getInt, getBoolean, ...) to return its private errno/message fields, which were not being cleared during local. Result: local $!; saved the old value but did not hide it, so any subsequent read still returned the stale errno.

This breaks the very common idiom used by File::BOM's _safe_read:

local $!;
my $status = read($fh, my $out, $count);
die $! if !$status && $!;

A stale $! from earlier in the program made every legitimate end-of-file read look like an error.

Minimal repro (before fix):

$ ./jperl -e '$! = 1; sub f { local $!; print "inside: $!\n" } f()'
inside: Operation not permitted    # bug — should be empty

After fix it prints inside: (empty), matching system Perl.

Test impact (running with ./jperl)

Module Before After
File::BOM t/01..bom.t dies after 41 tests 85 tests pass before unrelated POSIX::mkfifo cutoff
Text::AutoCSV (entire suite) 7/48 (cascade load failure) 535/560 (95.5%)
CSV::Processor blocked at load time loads; Utils.t passes

Test plan

  • make (full unit-test suite) passes locally
  • local $! smoke test matches system Perl
  • File::BOM t/01..bom.t reaches 85 passing tests (was 41)
  • Text::AutoCSV regains 535/560 passing tests
  • No other behavior changes — dynamicRestoreState already restores the saved errno/message from the existing stacks

Remaining issues (out of scope, separate tickets)

  • LWPx::TimedHTTP test suite uses fork (unimplemented in PerlOnJava); the eval { fork } SKIP guard doesn't trigger correctly and tests hang/fail. Blocks Email::Extractor, which in turn blocks CSV::Processor runtime tests.
  • File::BOM t/02..perlio-via.t uses PerlIO::via, already documented as unimplemented in dev/modules/perlio_via.md.
  • File::BOM 00..setup.t uses POSIX::mkfifo, also unimplemented (would let the remaining 30 tests run if available).
  • Email::Extractor has an undeclared Mail::Address dependency in its META — that's a CPAN module bug, not PerlOnJava.
  • CSV::Processor::Utils.pm emits a "my" variable $path masks earlier declaration warning — CPAN module's own code style.

Generated with Devin

fglock and others added 2 commits April 29, 2026 12:11
Two related fixes uncovered while investigating `jcpan -t CSV::Processor`,
which cascades through Email::Extractor, Text::AutoCSV, and File::BOM.

1. Encode.java: register `iso-10646-1` / `ISO-10646-1` as aliases for
   UCS-2 (UTF-16BE for the BMP), matching real Perl Encode's alias list.
   File::BOM uses this alias when building its enc2bom table; without it,
   `use File::BOM` died with "Unknown encoding: iso-10646-1" and the
   whole module failed to load (which in turn broke Text::AutoCSV, etc.).

2. ErrnoVariable.dynamicSaveState: after pushing the saved errno/message,
   reset the variable to the empty/no-error state so that `local $!;`
   actually clears $! inside the dynamic scope, the way Perl does. The
   parent `RuntimeScalar.dynamicSaveState` resets type/value to UNDEF,
   but ErrnoVariable's `toString()`/`getInt()` read its private `errno`
   and `message` fields, which were not being cleared. As a result,
   `local $!;` was a no-op for the visible value of $!.

   File::BOM relies on this in `_safe_read`:

       local $!;
       my $status = read($fh, my $out, $count);
       die $! if !$status && $!;

   With the bug, a stale $! from earlier in the program made every
   end-of-file read look like an error, killing t/01..bom.t at test 41.

Test impact (./jperl):
  - File::BOM t/01..bom.t: 41 → 85 passing tests (cutoff is now
    POSIX::mkfifo, an unrelated unimplemented function).
  - Text::AutoCSV: 7/48 → 535/560 passing tests (cascade unblocked).
  - CSV::Processor itself loads and its Utils.t passes; the remaining
    failure is the (already known) Email::Extractor → LWPx::TimedHTTP
    chain, which depends on `fork`.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Adds a real implementation of POSIX::mkfifo backed by libc's mkfifo()
on Linux and macOS via the FFM interface. On Windows there is no POSIX
FIFO concept, so the function returns -1 with errno=ENOSYS.

  - FFMPosixInterface: declare `int mkfifo(String path, int mode)`.
  - FFMPosixLinux: bind libc's `mkfifo` (used on macOS too via the
    inherited implementation). Resolution is guarded with `ifPresent`
    so initialization stays robust on hypothetical libcs without it.
  - FFMPosixWindows: stub that sets ENOSYS and returns -1.
  - POSIX.java: register `_mkfifo` -> posix_mkfifo. Returns "0 but
    true" on success and undef + sets $! on failure, matching real
    Perl's POSIX::mkfifo contract.
  - POSIX.pm: add `sub mkfifo { POSIX::_mkfifo(@_) }` wrapper next to
    the other file-management wrappers (mkdir, rmdir, link, ...).

Smoke test:

  $ ./jperl -MPOSIX -e '
      my $p = "/tmp/x.$$";
      POSIX::mkfifo($p, 0700) or die $!;
      print "is_fifo=", (-p $p ? 1 : 0), "\n";
      unlink $p;
      my $r = POSIX::mkfifo("/no/such/dir/x", 0700);
      print "fail=", (defined $r ? $r : "undef"), " errno=$!\n";
  '
  is_fifo=1
  fail=undef errno=No such file or directory

This unblocks File::BOM's t/00..setup.t (which previously aborted with
"Undefined subroutine &POSIX::mkfifo"). The next blocker on that test
path is `fork`, which is a separate, already-known limitation.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@fglock fglock force-pushed the fix/encode-iso10646-and-local-errno branch from 8b0c67f to 5d35b6c Compare April 29, 2026 10:12
@fglock fglock merged commit 517239c into master Apr 29, 2026
2 checks passed
@fglock fglock deleted the fix/encode-iso10646-and-local-errno branch April 29, 2026 10:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant