From 6e2f3aacef95e2c0fa60bbe1c5d6463ad30c6693 Mon Sep 17 00:00:00 2001 From: "Flavio S. Glock" Date: Wed, 25 Mar 2026 12:18:48 +0100 Subject: [PATCH 1/3] Fix MM->parse_version() for package-prefixed VERSION declarations The get_version() helper in ExtUtils::MM_Unix only matched $VERSION but not $Package::Name::VERSION. This caused modules like Dist::CheckConflicts that use the full package name in their version declaration to report version 'undef'. Updated the regex patterns to use /\$[\w:]*VERSION/ to match both: - $VERSION = '1.23' - $Dist::CheckConflicts::VERSION = '0.11' This fixes jcpan -t DateTime which was failing prerequisite checks due to incorrect version detection for Dist::CheckConflicts and CPAN::Meta::Check. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- src/main/java/org/perlonjava/core/Configuration.java | 2 +- src/main/perl/lib/ExtUtils/MM_Unix.pm | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/perlonjava/core/Configuration.java b/src/main/java/org/perlonjava/core/Configuration.java index 7fd9c27ab..c32b65609 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 = "217705588"; + public static final String gitCommitId = "3a54b2fee"; /** * Git commit date of the build (ISO format: YYYY-MM-DD). diff --git a/src/main/perl/lib/ExtUtils/MM_Unix.pm b/src/main/perl/lib/ExtUtils/MM_Unix.pm index 9d406b87d..229565c2e 100644 --- a/src/main/perl/lib/ExtUtils/MM_Unix.pm +++ b/src/main/perl/lib/ExtUtils/MM_Unix.pm @@ -57,12 +57,12 @@ sub get_version { $line = $1 if $line =~ m{^(.+)}s; # Directly extract version from common patterns - # Pattern 1: $VERSION = '1.23' or $VERSION = "1.23" - if ($line =~ /\$VERSION\s*=\s*['"]([^'"]+)['"]/) { + # Pattern 1: $VERSION = '1.23' or $Package::VERSION = '1.23' + if ($line =~ /\$[\w:]*VERSION\s*=\s*['"]([^'"]+)['"]/) { return $1; } # Pattern 2: $VERSION = 1.23 (bare number) - if ($line =~ /\$VERSION\s*=\s*([\d._]+)/) { + if ($line =~ /\$[\w:]*VERSION\s*=\s*([\d._]+)/) { return $1; } # Pattern 3: version->new('v1.2.3') or version->declare('v1.2.3') From ad0d59ddcd3cecb8cc49d1aebe9a8710bfc7fcfa Mon Sep 17 00:00:00 2001 From: "Flavio S. Glock" Date: Wed, 25 Mar 2026 12:33:16 +0100 Subject: [PATCH 2/3] Add module stubs for better CPAN prereq detection - re.pm: Add stub with VERSION for the built-in re pragma (implemented in Re.java). Fixes "re is not installed" prereq check errors. - Clone.pm: Add pure Perl fallback wrapper that loads Clone::PP when XSLoader fails. Clone is XS-only (no .pm files), so this provides a working Clone module for PerlOnJava. - open.pm: Add VERSION to stub for prereq detection. - B.pm: Fix VERSION to use numeric-only format (1.88) instead of 1.00_perlonjava which failed version->parse() validation. These changes fix `jcpan -t Specio` which was failing with: - Clone is not installed - re is not installed 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 | 2 +- src/main/perl/lib/B.pm | 2 +- src/main/perl/lib/Clone.pm | 62 +++++++++++++++++++ src/main/perl/lib/open.pm | 17 ++++- src/main/perl/lib/re.pm | 38 ++++++++++++ 5 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 src/main/perl/lib/Clone.pm create mode 100644 src/main/perl/lib/re.pm diff --git a/src/main/java/org/perlonjava/core/Configuration.java b/src/main/java/org/perlonjava/core/Configuration.java index c32b65609..029bf3349 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 = "3a54b2fee"; + public static final String gitCommitId = "6e2f3aace"; /** * Git commit date of the build (ISO format: YYYY-MM-DD). diff --git a/src/main/perl/lib/B.pm b/src/main/perl/lib/B.pm index 6e0233cff..2fdf19bb0 100644 --- a/src/main/perl/lib/B.pm +++ b/src/main/perl/lib/B.pm @@ -16,7 +16,7 @@ package B; use strict; use warnings; -our $VERSION = '1.00_perlonjava'; +our $VERSION = '1.88'; # Export functionality use Exporter 'import'; diff --git a/src/main/perl/lib/Clone.pm b/src/main/perl/lib/Clone.pm new file mode 100644 index 000000000..b01130bb7 --- /dev/null +++ b/src/main/perl/lib/Clone.pm @@ -0,0 +1,62 @@ +package Clone; + +use strict; +use warnings; + +require Exporter; + +our @ISA = qw(Exporter); +our @EXPORT; +our @EXPORT_OK = qw( clone ); + +our $VERSION = '0.49'; + +# PerlOnJava: Fall back to Clone::PP since we can't load XS +my $loaded = 0; + +eval { + require XSLoader; + XSLoader::load('Clone', $VERSION); + $loaded = 1; +}; + +if (!$loaded) { + # Fall back to pure Perl implementation + require Clone::PP; + *clone = \&Clone::PP::clone; +} + +1; +__END__ + +=head1 NAME + +Clone - recursively copy Perl datatypes + +=head1 SYNOPSIS + + use Clone 'clone'; + + my $data = { + set => [ 1 .. 50 ], + foo => { + answer => 42, + object => SomeObject->new, + }, + }; + + my $cloned_data = clone($data); + +=head1 DESCRIPTION + +This module provides a C method which makes recursive +copies of nested hash, array, scalar and reference types, +including tied variables and objects. + +PerlOnJava uses Clone::PP (pure Perl implementation) as the backend. + +=head1 SEE ALSO + +L, L + +=cut diff --git a/src/main/perl/lib/open.pm b/src/main/perl/lib/open.pm index 172d831cf..50ce61a89 100644 --- a/src/main/perl/lib/open.pm +++ b/src/main/perl/lib/open.pm @@ -1,5 +1,20 @@ package open; -# placeholder +# Stub for open pragma - sets default I/O layers +# PerlOnJava implementation by Flavio S. Glock + +use strict; +use warnings; + +our $VERSION = '1.14'; + +# The open pragma sets default PerlIO layers for input/output +# In PerlOnJava, UTF-8 is the default encoding + +sub import { + my $class = shift; + # For now, accept but ignore layer specifications + # PerlOnJava defaults to UTF-8 encoding +} 1; diff --git a/src/main/perl/lib/re.pm b/src/main/perl/lib/re.pm new file mode 100644 index 000000000..b7a5fbd06 --- /dev/null +++ b/src/main/perl/lib/re.pm @@ -0,0 +1,38 @@ +package re; + +# Stub for re pragma - actual implementation is in Java (Re.java) +# This file exists for version detection by MM->parse_version() + +use strict; +use warnings; + +our $VERSION = "0.48"; + +# The Java implementation (Re.java) handles: +# - use re '/a', '/aa', '/u' - regex character class modifiers +# - use re 'strict' - enable experimental regex warnings +# - re::is_regexp($ref) - check if reference is compiled regex + +1; + +__END__ + +=head1 NAME + +re - Perl pragma to alter regular expression behaviour + +=head1 SYNOPSIS + + use re '/a'; # ASCII-restrict \w, \d, \s, \b + use re '/aa'; # ASCII-restrict including case folding + use re '/u'; # Unicode semantics + use re 'strict'; # Enable experimental regex warnings + + if (re::is_regexp($ref)) { ... } + +=head1 DESCRIPTION + +This is the PerlOnJava implementation of the C pragma. +See L for the full Perl documentation. + +=cut From 490e00075c014b0afeef792d8125247ddd546dc5 Mon Sep 17 00:00:00 2001 From: "Flavio S. Glock" Date: Wed, 25 Mar 2026 12:41:27 +0100 Subject: [PATCH 3/3] Fix %INC to use proper paths for built-in modules with .pm stubs When a built-in Java module (like UNIVERSAL, re, Clone) has a .pm stub file in the bundled lib, set %INC to the jar:PERL5LIB path format instead of just the module name. This allows code that opens %INC entries (like Test::Specio) to find a real file. Changes: - PerlModuleBase.initializeModule(): Check if .pm stub exists in JAR, use jar:PERL5LIB/Module.pm format if so - UNIVERSAL.pm: New stub file with VERSION for prereq detection This fixes Test::Specio tests that were failing with: "Could not open UNIVERSAL.pm for the test" 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 | 2 +- .../runtime/perlmodule/PerlModuleBase.java | 19 +++++- src/main/perl/lib/UNIVERSAL.pm | 64 +++++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/main/perl/lib/UNIVERSAL.pm diff --git a/src/main/java/org/perlonjava/core/Configuration.java b/src/main/java/org/perlonjava/core/Configuration.java index 029bf3349..acbc7dcd4 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 = "6e2f3aace"; + public static final String gitCommitId = "ad0d59ddc"; /** * Git commit date of the build (ISO format: YYYY-MM-DD). diff --git a/src/main/java/org/perlonjava/runtime/perlmodule/PerlModuleBase.java b/src/main/java/org/perlonjava/runtime/perlmodule/PerlModuleBase.java index 86a08faf1..967433a1a 100644 --- a/src/main/java/org/perlonjava/runtime/perlmodule/PerlModuleBase.java +++ b/src/main/java/org/perlonjava/runtime/perlmodule/PerlModuleBase.java @@ -4,6 +4,7 @@ import org.perlonjava.runtime.runtimetypes.*; import java.lang.invoke.MethodHandle; +import java.net.URL; /** * Abstract base class for Perl modules in the Java environment. @@ -35,10 +36,26 @@ public PerlModuleBase(String moduleName, boolean setInc) { /** * Initializes the Perl module by setting the %INC hash to indicate * that the module is loaded. + * If a .pm stub file exists in the JAR, use the jar:PERL5LIB path format + * so that code opening %INC entries can find a real file. */ private void initializeModule() { + String pmFileName = moduleName.replace("::", "/") + ".pm"; + String incValue; + + // Check if there's a .pm file in the bundled lib (JAR) + String resourcePath = "/lib/" + pmFileName; + URL resource = PerlModuleBase.class.getResource(resourcePath); + if (resource != null) { + // Use jar:PERL5LIB path format - this can be opened by the runtime + incValue = GlobalContext.JAR_PERLLIB + "/" + pmFileName; + } else { + // No .pm stub file - use simple name (backwards compatible) + incValue = moduleName + ".pm"; + } + // Set %INC to indicate the module is loaded - GlobalVariable.getGlobalHash("main::INC").put(moduleName.replace("::", "/") + ".pm", new RuntimeScalar(moduleName + ".pm")); + GlobalVariable.getGlobalHash("main::INC").put(pmFileName, new RuntimeScalar(incValue)); } /** diff --git a/src/main/perl/lib/UNIVERSAL.pm b/src/main/perl/lib/UNIVERSAL.pm new file mode 100644 index 000000000..160bf2db9 --- /dev/null +++ b/src/main/perl/lib/UNIVERSAL.pm @@ -0,0 +1,64 @@ +package UNIVERSAL; + +# Stub for UNIVERSAL module - actual implementation is in Java (Universal.java) +# This file exists so %INC has a real file path that can be opened by tests + +use strict; +use warnings; + +our $VERSION = '1.17'; + +# The Java implementation (Universal.java) provides: +# - can($method) - check if object/class can perform method +# - isa($class) - check if object/class is or inherits from class +# - DOES($role) - check if object/class does a role (same as isa) +# - VERSION([$require]) - get/check package version + +1; + +__END__ + +=head1 NAME + +UNIVERSAL - base class for ALL classes (blessed references) + +=head1 SYNOPSIS + + $obj->isa('ClassName'); + $obj->can('method'); + $obj->DOES('RoleName'); + $obj->VERSION; + + ClassName->isa('ParentClass'); + ClassName->can('method'); + ClassName->VERSION($required); + +=head1 DESCRIPTION + +UNIVERSAL is the base class which all blessed references inherit from. +This is the PerlOnJava implementation. + +=head1 METHODS + +=over 4 + +=item $obj->isa(TYPE) + +Returns true if $obj is blessed into package TYPE or inherits from TYPE. + +=item $obj->can(METHOD) + +Returns a reference to the method if $obj can perform METHOD, false otherwise. + +=item $obj->DOES(ROLE) + +Returns true if $obj does the role ROLE. In PerlOnJava, this is equivalent to isa(). + +=item $obj->VERSION([REQUIRE]) + +Returns the version of the package $obj is blessed into. If REQUIRE is given, +dies if the version is less than REQUIRE. + +=back + +=cut