Skip to content

Add CPAN Phase 1-3: Core modules and IPC::Open2/Open3#313

Merged
fglock merged 13 commits into
masterfrom
feature/cpan-phase1-modules
Mar 13, 2026
Merged

Add CPAN Phase 1-3: Core modules and IPC::Open2/Open3#313
fglock merged 13 commits into
masterfrom
feature/cpan-phase1-modules

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Mar 13, 2026

Summary

Implements CPAN client support phases 1-3, adding essential Perl modules for CPAN compatibility.

Phase 1: Low-hanging fruit

  • DirHandle, Dumpvalue - imported via sync.pl
  • Sys::Hostname - XS module with Java InetAddress
  • flock() - file locking via java.nio.channels.FileLock
  • syscall() - SYS_gethostname support

Phase 2: Archive/Network modules

  • IO::Socket, IO::Socket::INET, IO::Socket::UNIX
  • IO::Zlib, Archive::Tar
  • Net::FTP, Net::Cmd and libnet modules
  • Symbol.pm imported from CPAN

Phase 3: Process Control

  • IPC::Open2, IPC::Open3 - custom Java implementation using ProcessBuilder
  • fcntl(), ioctl() - stub + native via jnr-posix
  • pipe() autovivification fix

PerlSubroutine Functional Interface

Fixes MethodHandle conversion errors by replacing MethodHandle-based subroutine
invocation with a type-safe PerlSubroutine functional interface:

  • Generated Perl classes now implement PerlSubroutine interface
  • InterpretedCode implements PerlSubroutine interface
  • RuntimeCode.apply() prefers subroutine.apply() over methodHandle.invoke()
  • Inline cache (callCached) updated to use functional interface
  • comp/require.t now passes 1743/1747 tests with no MethodHandle errors

Bug fixes

  • Reference comparison \$x == \undef no longer crashes (NPE fix)
  • Regex octal escapes \10-\377 now work correctly
  • Parser: keywords after :: in package variables
  • Parser: @{${...}} nested dereference in push/unshift
  • Prototype parsing for typeglob arguments

New files

  • IPCOpen3.java - XS module for open2/open3
  • ProcessInputHandle.java, ProcessOutputHandle.java - process stream I/O
  • SysHostname.java - XS module for Sys::Hostname
  • PerlSubroutine.java - functional interface for subroutine dispatch

Test plan

  • ./gradlew test passes
  • IPC::Open2 tested with cat command
  • IPC::Open3 tested with separate stdout/stderr
  • comp/require.t passes without MethodHandle conversion errors
  • caller() works correctly in both JVM and interpreter backends
  • Works on POSIX (macOS tested)

Generated with Devin

fglock and others added 13 commits March 13, 2026 20:18
Phase 3 CPAN work:
- Import IPC::Open2.pm and IPC::Open3.pm from perl5 tree
- Implement pipe() autovivification like open() does
- Add fcntl() and ioctl() operators (stub + native via jnr-posix)
- Fix prototype parsing for typeglob arguments (use =~ precedence)
- Fix RuntimeScalar.getIntRef()/getDoubleRef() to use this.hashCode()
  instead of value.hashCode() - fixes NPE when comparing \undef and
  makes reference numeric values consistent with other types

The reference fix resolves the crash: `\$_[0] == \undef`
which is used in IPC::Open3.pm to detect literal undef arguments.

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

Co-Authored-By: Devin <noreply@cognition.ai>
Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
- Add IPCOpen3.java XS module loaded via XSLoader
- Create ProcessInputHandle and ProcessOutputHandle for process I/O
- Custom Open2.pm and Open3.pm wrappers (not imported from perl5)
- Works on both Windows and POSIX (uses WaitpidOperator on Windows)
- Supports separate stdout/stderr handles in open3
- Remove IPC::Open2/Open3 from sync.pl config to prevent overwrite

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

Co-Authored-By: Devin <noreply@cognition.ai>
Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
pipe() should fail when passed a reference to a scalar.
This fixes io/pvbm.t tests 6-7.

Net change: +1 test passing (26/28 -> 27/28)
- Tests 4,5 now pass (pipe with PVBM works)
- Tests 6,7 now pass (pipe with reference fails)
- Test 16 now fails (side effect: $pvbm becomes filehandle)

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

Co-Authored-By: Devin <noreply@cognition.ai>
This replaces MethodHandle-based invocation with a type-safe functional
interface, fixing MethodHandle conversion errors that occurred at runtime.

Key changes:
- Create PerlSubroutine @FunctionalInterface with apply(RuntimeArray, int)
- EmitterMethodCreator: generated classes implement PerlSubroutine
- InterpretedCode: implements PerlSubroutine interface
- RuntimeCode: add subroutine field, prefer it over methodHandle in apply()
- SubroutineParser: set subroutine field for deferred compilation
- callCached(): prefer subroutine.apply() in inline cache

The methodHandle field is kept for backward compatibility with PerlModuleBase
which still uses it to preserve caller() stack behavior.

comp/require.t now passes 1743/1747 tests with no MethodHandle conversion errors.

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

Co-Authored-By: Devin <noreply@cognition.ai>
Since we now set the subroutine field (PerlSubroutine interface),
the methodHandle lookups are no longer needed for deferred compilation.
Both JVM-compiled and InterpretedCode paths now use subroutine directly.

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

Co-Authored-By: Devin <noreply@cognition.ai>
Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
- InternalPipeHandle.doRead(): Always use polling instead of blocking reads
  PipedInputStream.read() uses Object.wait() which does not respond to
  Thread.interrupt(), so we poll with available() and short sleeps

- InternalPipeHandle.syswrite(): Add implementation for system-level writes
  Previously missing, caused syswrite to fail on pipes

- InternalPipeHandle.sysread(): Use same polling approach

This fixes op/readline.t tests that use pipe() with alarm() to test
interrupted reads. Test results improved from 9/36 to 19/36 passing.

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

Co-Authored-By: Devin <noreply@cognition.ai>
- readline() on undefined filehandle now warns and returns undef instead
  of throwing exception (matches Perl behavior)
- defined *$var now works under strict refs without throwing error
  (Perl allows this as a way to probe glob existence)
- Added DEFINED_GLOB opcode (386) for interpreter backend
- Added GlobalVariable.definedGlob() to check glob slots without auto-vivifying
- Added RuntimeGlob.defined() to check if any slot has content

Test improvements:
- op/readline.t: 19 -> 23 passing tests
- Tests 26-27 now pass (autovivification checks)

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

Co-Authored-By: Devin <noreply@cognition.ai>
The fix for 'defined *$var' under strict refs incorrectly checked
if the scalar VALUE was defined. In Perl, 'defined *glob' returns
true if the glob exists (any slot is initialized), not if the
scalar value is defined.

Changes:
- GlobalVariable.definedGlob(): Check slot existence, not value definedness
- RuntimeGlob.defined(): Same fix for glob references
- Recognize numeric capture variables ($1, $42, etc.) algorithmically
- Initialize additional magic variables: ${^UTF8LOCALE}, ${^WARNING_BITS},
  ${^UTF8CACHE}, $[, $~, $%, $1-$9

This restores op/magic.t from 129 to 170 passing tests.

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

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock merged commit aff1886 into master Mar 13, 2026
2 checks passed
@fglock fglock deleted the feature/cpan-phase1-modules branch March 13, 2026 22:19
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