diff --git a/docs/about/changelog.md b/docs/about/changelog.md index d1746669a..9247d1ab1 100644 --- a/docs/about/changelog.md +++ b/docs/about/changelog.md @@ -7,6 +7,7 @@ Release history of PerlOnJava. See [Roadmap](roadmap.md) for future plans. - Fix localization of numbered regex captures. - Fix IO-handle type checks and uninitialized-value warning locations. - Fix numeric-zero results from failed `s///` substitutions. +- Preserve references in `utf8::downgrade`. - Bundle the complete CPAN `File::Path` 2.18 implementation, including modern `rmtree`/`remove_tree` options such as `keep_root`, `error`, `result`, `safe`, and `verbose`. diff --git a/src/main/java/org/perlonjava/runtime/perlmodule/Utf8.java b/src/main/java/org/perlonjava/runtime/perlmodule/Utf8.java index 9f6ce336a..fe15f351d 100644 --- a/src/main/java/org/perlonjava/runtime/perlmodule/Utf8.java +++ b/src/main/java/org/perlonjava/runtime/perlmodule/Utf8.java @@ -172,6 +172,14 @@ public static RuntimeList downgrade(RuntimeArray args, int ctx) { RuntimeScalar scalar = args.get(0); boolean wasTainted = GlobalContext.isTaintModeActive() && scalar.isTainted(); boolean failOk = args.size() == 2 && args.get(1).getBoolean(); + + // References do not have a UTF-8 flag. Perl treats downgrade() on a + // reference as a successful no-op; converting it through toString() + // would replace the reference with its stringified form. + if (RuntimeScalarType.isReference(scalar)) { + return RuntimeScalarCache.scalarTrue.getList(); + } + String string = scalar.toString(); // Check if the string can be represented in ISO-8859-1 diff --git a/src/test/resources/unit/utf8_downgrade_references.t b/src/test/resources/unit/utf8_downgrade_references.t new file mode 100644 index 000000000..60246d424 --- /dev/null +++ b/src/test/resources/unit/utf8_downgrade_references.t @@ -0,0 +1,23 @@ +use strict; +use warnings; +use Test::More; + +my $scalar = 'value'; +my $object = bless {}, 'Local::Downgrade::Object'; +my @references = ( + [ CODE => sub { 42 } ], + [ ARRAY => [] ], + [ HASH => {} ], + [ SCALAR => \$scalar ], + [ 'Local::Downgrade::Object' => $object ], +); + +for my $case (@references) { + my ($expected_type, $value) = @$case; + is(utf8::downgrade($value, 1), 1, "downgrade succeeds for $expected_type reference"); + is(ref($value), $expected_type, "downgrade preserves $expected_type reference"); +} + +is($references[0][1]->(), 42, 'downgrade preserves a callable code reference'); + +done_testing;