From 67bdcaf8e02bd67509206924d267fe67e8992543 Mon Sep 17 00:00:00 2001 From: "Flavio S. Glock" Date: Wed, 29 Apr 2026 13:51:05 +0200 Subject: [PATCH] fix(runtime): values() in scalar context returns count, not last element Previously, `scalar(values %h)` worked when called directly, but `return values %h` from a sub collapsed to the last value instead of the count when the caller was in scalar context. Same for arrays. Root cause: RuntimeHash.keys() set scalarContextSize on the returned RuntimeArray so list-to-scalar conversion later yields the count. RuntimeHash.values() and RuntimeArray.values() did not, so any list-collapse path (sub return, comma operator) used last-element semantics. Fix: - RuntimeHash.values() sets scalarContextSize on the returned array for both regular and TIED_HASH paths. - RuntimeArray.values() returns a fresh array aliasing the elements (so `for (values @a) { $_++ }` still mutates @a) with scalarContextSize set, so the original @a's scalar-context behaviour is unaffected. Surfaced by Moose's `Class::MOP::Class::get_all_attributes` ending in `return values %attrs`, which made every `scalar $meta->get_all_attributes` return a stringified Attribute instead of a count, breaking 3 subtests in MooseX::Role::Parameterized's t/001-parameters.t. 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 | 4 ++-- .../perlonjava/runtime/runtimetypes/RuntimeArray.java | 9 ++++++++- .../org/perlonjava/runtime/runtimetypes/RuntimeHash.java | 4 ++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/perlonjava/core/Configuration.java b/src/main/java/org/perlonjava/core/Configuration.java index 4fd48602a..462a754ac 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 = "bebebd07e"; + public static final String gitCommitId = "517239c4e"; /** * Git commit date of the build (ISO format: YYYY-MM-DD). @@ -48,7 +48,7 @@ public final class Configuration { * Parsed by App::perlbrew and other tools via: perl -V | grep "Compiled at" * DO NOT EDIT MANUALLY - this value is replaced at build time. */ - public static final String buildTimestamp = "Apr 29 2026 12:11:44"; + public static final String buildTimestamp = "Apr 29 2026 13:50:07"; // Prevent instantiation private Configuration() { diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeArray.java b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeArray.java index e71994d49..ca89c352a 100644 --- a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeArray.java +++ b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeArray.java @@ -1156,7 +1156,14 @@ public RuntimeArray keys() { public RuntimeArray values() { // Reset the each iterator when values() is called this.eachIteratorIndex = null; - return this; + // Return a new RuntimeArray that aliases the elements but carries + // scalarContextSize so values() in scalar context yields the count + // (matching real Perl). We do not set scalarContextSize on `this` + // because that would persist and corrupt later scalar(@arr) results. + RuntimeArray result = new RuntimeArray(); + result.elements.addAll(this.elements); + result.scalarContextSize = this.elements.size(); + return result; } /** diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeHash.java b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeHash.java index 4d80cbda7..350206e94 100644 --- a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeHash.java +++ b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeHash.java @@ -924,6 +924,8 @@ public RuntimeArray values() { isKey = !isKey; } hashIterator = null; // keys resets the iterator + // Set scalarContextSize so that values() in scalar context returns the count + list.scalarContextSize = list.elements.size(); return list; } @@ -936,6 +938,8 @@ public RuntimeArray values() { list.elements.add(value); // push an alias to the value (direct reference, not a copy) } hashIterator = null; // values resets the iterator + // Set scalarContextSize so that values() in scalar context returns the count + list.scalarContextSize = list.elements.size(); return list; }