Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions dev/design/moo_support.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
86 changes: 86 additions & 0 deletions src/main/java/org/perlonjava/runtime/perlmodule/SubName.java
Original file line number Diff line number Diff line change
@@ -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 <a href="https://metacpan.org/pod/Sub::Name">Sub::Name on CPAN</a>
*/
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();
}
}
80 changes: 80 additions & 0 deletions src/main/perl/lib/Sub/Name.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package Sub::Name;
# ABSTRACT: (Re)name a sub

#
# PerlOnJava implementation of Sub::Name
# Original module by Matthijs van Duin <xmath@cpan.org>
#
# 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<Sub::Identify> - for getting information about subs

=item *

L<Sub::Util> - set_subname is another implementation of C<subname>

=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
Loading