From 86591c703ec8562d357ddf5271c39554ffcb72b7 Mon Sep 17 00:00:00 2001 From: "Flavio S. Glock" Date: Mon, 16 Mar 2026 12:00:53 +0100 Subject: [PATCH 1/2] Add Sub::Name module and fix @INC hook exception handling - Add SubName.java: Java implementation of Sub::Name::subname() - Add Sub/Name.pm: Perl wrapper using XSLoader - Fix ModuleOperators.java: Propagate exceptions from @INC hooks The @INC hook fix is required because InlineModule (used by Moo tests) relies on die() in hooks to hide modules. Previously, exceptions were caught and ignored, causing require to continue searching. Sub::Name::subname(NAME, CODE) is equivalent to Sub::Util::set_subname() and is used by Moo for debugging/caller information. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin --- .../runtime/operators/ModuleOperators.java | 17 ++-- .../runtime/perlmodule/SubName.java | 86 +++++++++++++++++++ src/main/perl/lib/Sub/Name.pm | 80 +++++++++++++++++ 3 files changed, 173 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/perlonjava/runtime/perlmodule/SubName.java create mode 100644 src/main/perl/lib/Sub/Name.pm diff --git a/src/main/java/org/perlonjava/runtime/operators/ModuleOperators.java b/src/main/java/org/perlonjava/runtime/operators/ModuleOperators.java index da9841714..d0d8068e7 100644 --- a/src/main/java/org/perlonjava/runtime/operators/ModuleOperators.java +++ b/src/main/java/org/perlonjava/runtime/operators/ModuleOperators.java @@ -862,18 +862,15 @@ else if (hook.type == RuntimeScalarType.ARRAYREFERENCE && hook.value instanceof args.push(selfArg); args.push(new RuntimeScalar(fileName)); - try { - RuntimeBase result = codeRef.apply(args, RuntimeContextType.SCALAR); - - // If result is undef, return null to continue to next @INC entry - if (result == null || !result.scalar().defined().getBoolean()) { - return null; - } + // Call the hook - if it throws an exception, propagate it + // This matches Perl's behavior where die() in an @INC hook stops require + RuntimeBase result = codeRef.apply(args, RuntimeContextType.SCALAR); - return result; - } catch (Exception e) { - // If hook throws an exception, continue to next @INC entry + // If result is undef, return null to continue to next @INC entry + if (result == null || !result.scalar().defined().getBoolean()) { return null; } + + return result; } } \ No newline at end of file diff --git a/src/main/java/org/perlonjava/runtime/perlmodule/SubName.java b/src/main/java/org/perlonjava/runtime/perlmodule/SubName.java new file mode 100644 index 000000000..28aaa4993 --- /dev/null +++ b/src/main/java/org/perlonjava/runtime/perlmodule/SubName.java @@ -0,0 +1,86 @@ +package org.perlonjava.runtime.perlmodule; + +import org.perlonjava.runtime.runtimetypes.*; + +import static org.perlonjava.runtime.runtimetypes.RuntimeScalarType.*; + +/** + * Sub::Name module implementation for PerlOnJava. + * Provides the subname() function to (re)name a subroutine. + * + * This is compatible with the CPAN Sub::Name module. + * The subname is only used for informative routines (caller, Carp, etc). + * + * @see Sub::Name on CPAN + */ +public class SubName extends PerlModuleBase { + + /** + * Constructor for SubName. + */ + public SubName() { + super("Sub::Name", false); // false because loaded via XSLoader + } + + /** + * Static initializer called by XSLoader::load(). + */ + public static void initialize() { + SubName subName = new SubName(); + subName.initializeExporter(); + // Set $VERSION to match CPAN version + GlobalVariable.getGlobalVariable("Sub::Name::VERSION").set(new RuntimeScalar("0.28")); + // subname is exported by default + subName.defineExport("EXPORT", "subname"); + subName.defineExport("EXPORT_OK", "subname"); + try { + subName.registerMethod("subname", null); // No prototype to allow flexible args + } catch (NoSuchMethodException e) { + System.err.println("Warning: Missing Sub::Name method: " + e.getMessage()); + } + } + + /** + * subname NAME, CODEREF + * + * Assigns a new name to referenced sub. If package specification is omitted in + * the name, then the current package is used. The return value is the sub. + * + * The name is only used for informative routines (caller, Carp, etc). You won't + * be able to actually invoke the sub by the given name. To allow that, you need + * to do glob-assignment yourself. + * + * @param args The arguments: name string, CODE reference + * @param ctx The context + * @return The CODE reference + */ + public static RuntimeList subname(RuntimeArray args, int ctx) { + if (args.size() != 2) { + throw new IllegalStateException("Usage: subname(name, coderef)"); + } + RuntimeScalar nameScalar = args.get(0); + RuntimeScalar codeRef = args.get(1); + + if (codeRef.type != CODE) { + throw new IllegalArgumentException("Not a subroutine reference"); + } + + RuntimeCode code = (RuntimeCode) codeRef.value; + String fullName = nameScalar.toString(); + + // Parse package::subname format + int lastColon = fullName.lastIndexOf("::"); + if (lastColon >= 0) { + code.packageName = fullName.substring(0, lastColon); + code.subName = fullName.substring(lastColon + 2); + } else { + // No package specified - use current package from caller + RuntimeList callerInfo = RuntimeCode.caller(new RuntimeList(), RuntimeContextType.LIST); + if (!callerInfo.isEmpty()) { + code.packageName = callerInfo.elements.get(0).toString(); + } + code.subName = fullName; + } + return codeRef.getList(); + } +} diff --git a/src/main/perl/lib/Sub/Name.pm b/src/main/perl/lib/Sub/Name.pm new file mode 100644 index 000000000..b49a744e9 --- /dev/null +++ b/src/main/perl/lib/Sub/Name.pm @@ -0,0 +1,80 @@ +package Sub::Name; +# ABSTRACT: (Re)name a sub + +# +# PerlOnJava implementation of Sub::Name +# Original module by Matthijs van Duin +# +# The implementation is in: +# src/main/java/org/perlonjava/runtime/perlmodule/SubName.java +# + +use strict; +use warnings; + +our $VERSION = '0.28'; + +use Exporter (); +*import = \&Exporter::import; + +our @EXPORT = qw(subname); +our @EXPORT_OK = @EXPORT; + +XSLoader::load('Sub::Name', $VERSION); + +1; + +__END__ + +=head1 NAME + +Sub::Name - (Re)name a sub + +=head1 SYNOPSIS + + use Sub::Name; + + subname $name, $subref; + + $subref = subname foo => sub { ... }; + +=head1 DESCRIPTION + +This module has only one function, which is also exported by default: + +=head2 subname NAME, CODEREF + +Assigns a new name to referenced sub. If package specification is omitted in +the name, then the current package is used. The return value is the sub. + +The name is only used for informative routines (caller, Carp, etc). You won't +be able to actually invoke the sub by the given name. To allow that, you need +to do glob-assignment yourself. + +Note that for anonymous closures (subs that reference lexicals declared outside +the sub itself) you can name each instance of the closure differently, which +can be very useful for debugging. + +=head1 SEE ALSO + +=over 4 + +=item * + +L - for getting information about subs + +=item * + +L - set_subname is another implementation of C + +=back + +=head1 COPYRIGHT AND LICENSE + +This software is copyright (c) 2004, 2008 by Matthijs van Duin, all rights reserved; +copyright (c) 2014 cPanel Inc., all rights reserved. + +This program is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut From a5cf816ff21f21eda0cdc06538999cc3b2a46302 Mon Sep 17 00:00:00 2001 From: "Flavio S. Glock" Date: Mon, 16 Mar 2026 12:02:37 +0100 Subject: [PATCH 2/2] Update Moo design doc with Phase 26 (Sub::Name) - Add Phase 26: Sub::Name module and @INC hook fix - Remove moo-utils-_subname-Sub-Name.t from expected failures - Update test counts: 63/71 Moo tests passing Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin --- dev/design/moo_support.md | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/dev/design/moo_support.md b/dev/design/moo_support.md index 8b58febb0..a28be37dc 100644 --- a/dev/design/moo_support.md +++ b/dev/design/moo_support.md @@ -612,26 +612,36 @@ Moo tests run via `jcpan -t Moo`. Recent fixes (Phases 12-13) should improve pas - This fixed Mo's BUILD feature which depends on the %e merge pattern - Mo tests: 6/28 failing → 1/28 failing (143/144 subtests pass) +- [x] Phase 26: Add Sub::Name module and fix @INC hook exception handling (2026-03-16) + - Root cause: moo-utils-_subname-Sub-Name.t was failing because: + 1. Sub::Name module was not available + 2. @INC hooks that threw exceptions were silently ignored + - **SubName.java**: Java implementation of Sub::Name::subname(NAME, CODEREF) + - **Sub/Name.pm**: Perl wrapper using XSLoader + - **ModuleOperators.java fix**: + - Previously: catch (Exception e) { return null; } - ignored hook errors + - Now: Let exceptions propagate to match Perl's behavior + - This allows InlineModule to "hide" modules by having hooks throw die() + - moo-utils-_subname-Sub-Name.t: 0/2 → 2/2 passing + ### Current Status -**Test Results (after Phase 25):** -- **Moo**: 62/71 test programs passing (87%), 768/829 subtests passing (93%) +**Test Results (after Phase 26):** +- **Moo**: 63/71 test programs passing (89%), 770/829 subtests passing (93%) - **Mo**: 27/28 test programs passing (99.3%), 143/144 subtests passing **Remaining Failures (categorized):** 1. **accessor-weaken tests** (20 failures) - Expected, weak references not supported in Java GC 2. **croak-locations.t** (29 failures) - Carp reports `(eval N)` instead of actual filename 3. **demolish tests** (6 failures) - Expected, DESTROY not supported -4. **moo-utils-_subname-Sub-Name.t** (1 failure) - Expected, we have Sub::Util (no fallback to Sub::Name) -5. **no-moo.t** (5 failures) - Namespace cleanup requires weak references -6. **overloaded-coderefs.t** - Expected, B::Deparse not available -7. **Mo t/strict.t** (1 failure) - Error message format differs from Perl +4. **no-moo.t** (5 failures) - Namespace cleanup requires weak references +5. **overloaded-coderefs.t** - Expected, B::Deparse not available +6. **Mo t/strict.t** (1 failure) - Error message format differs from Perl **Expected failures** (not fixable without fundamental changes): - Weak references: accessor-weaken tests (20), no-moo.t cleanup (5) - DESTROY/GC: demolish tests (6) - Missing B::Deparse: overloaded-coderefs.t -- Sub::Name fallback: moo-utils-_subname-Sub-Name.t (1) **Potentially fixable**: - croak-locations.t (29) - Carp filename in string eval @@ -646,6 +656,7 @@ Moo tests run via `jcpan -t Moo`. Recent fixes (Phases 12-13) should improve pas - **Branch**: `feature/moo-support` (PR #319 - merged) - **Branch**: `fix/goto-tailcall-import` (PR #320 - open) - **Branch**: `fix/mo-bareword-parsing` (PR #322 - open) +- **Branch**: `feature/sub-name` (PR #324 - open) - **Key commits**: - `00c124167` - Fix print { func() } filehandle block parsing and JVM codegen - `393bedf0f` - Fix quotemeta and Package::SUPER::method resolution @@ -655,6 +666,7 @@ Moo tests run via `jcpan -t Moo`. Recent fixes (Phases 12-13) should improve pas - `db434f8d3` - Fix ::identifier bareword parsing and add cpan to sync - `ff31163f9` - Fix self-referential hash assignment %h = (stuff, %h) - `a3233cd55` - Improve ::identifier to check sub existence at compile time + - `86591c703` - Add Sub::Name module and fix @INC hook exception handling ## Related Documents