diff --git a/src/main/java/org/perlonjava/operators/ReferenceOperators.java b/src/main/java/org/perlonjava/operators/ReferenceOperators.java index 7b54f1a7..01ea0f42 100644 --- a/src/main/java/org/perlonjava/operators/ReferenceOperators.java +++ b/src/main/java/org/perlonjava/operators/ReferenceOperators.java @@ -62,6 +62,13 @@ public static RuntimeScalar ref(RuntimeScalar runtimeScalar) { RuntimeGlob glob = (RuntimeGlob) runtimeScalar.value; String globName = glob.globName; + // Special case: stash entries (RuntimeStashEntry) should always return empty string + // because they represent stash entries, not regular globs + if (runtimeScalar.value instanceof RuntimeStashEntry) { + str = ""; + break; + } + // Special case: stash globs (ending with ::) should always return empty string // because they represent the entire package stash, not a single slot if (globName.endsWith("::")) { diff --git a/src/main/java/org/perlonjava/runtime/HashSpecialVariable.java b/src/main/java/org/perlonjava/runtime/HashSpecialVariable.java index a82d1b20..2f4e782f 100644 --- a/src/main/java/org/perlonjava/runtime/HashSpecialVariable.java +++ b/src/main/java/org/perlonjava/runtime/HashSpecialVariable.java @@ -59,7 +59,7 @@ public HashSpecialVariable(HashSpecialVariable.Id mode, String namespace) { * @return A RuntimeHash object representing the stash. */ public static RuntimeHash getStash(String namespace) { - return new RuntimeStash(namespace); + return GlobalVariable.getGlobalHash(namespace); } /** diff --git a/src/main/java/org/perlonjava/runtime/RuntimeStash.java b/src/main/java/org/perlonjava/runtime/RuntimeStash.java index 634a8ff6..1c4ccd44 100644 --- a/src/main/java/org/perlonjava/runtime/RuntimeStash.java +++ b/src/main/java/org/perlonjava/runtime/RuntimeStash.java @@ -90,7 +90,28 @@ public void put(String key, RuntimeScalar value) { */ public RuntimeScalar get(String key) { if (!elements.containsKey(key)) { - return new RuntimeStashEntry(namespace + key, false); + // Check if any slots exist for this glob name + String fullKey = namespace + key; + + // Check if the variable exists by trying to get it and checking if it's defined + RuntimeScalar var = GlobalVariable.getGlobalVariable(fullKey); + boolean hasScalarSlot = var.getDefinedBoolean(); + + boolean hasArraySlot = GlobalVariable.existsGlobalArray(fullKey); + boolean hasHashSlot = GlobalVariable.existsGlobalHash(fullKey); + + RuntimeScalar codeRef = GlobalVariable.getGlobalCodeRef(fullKey); + boolean hasCodeSlot = codeRef.type == RuntimeScalarType.CODE && codeRef.getDefinedBoolean(); + + RuntimeScalar ioRef = GlobalVariable.getGlobalIO(fullKey); + boolean hasIOSlot = ioRef.type == RuntimeScalarType.GLOB && ioRef.value instanceof RuntimeIO; + + RuntimeScalar formatRef = GlobalVariable.getGlobalFormatRef(fullKey); + boolean hasFormatSlot = formatRef.type == RuntimeScalarType.FORMAT && formatRef.getDefinedBoolean(); + + boolean hasSlots = hasScalarSlot || hasArraySlot || hasHashSlot || hasCodeSlot || hasIOSlot || hasFormatSlot; + + return new RuntimeStashEntry(namespace + key, hasSlots); } return new RuntimeStashEntry(namespace + key, true); } diff --git a/src/main/java/org/perlonjava/runtime/RuntimeStashEntry.java b/src/main/java/org/perlonjava/runtime/RuntimeStashEntry.java index cf6984bb..fa87887e 100644 --- a/src/main/java/org/perlonjava/runtime/RuntimeStashEntry.java +++ b/src/main/java/org/perlonjava/runtime/RuntimeStashEntry.java @@ -279,4 +279,18 @@ public RuntimeStashEntry undefine() { return this; } + /** + * Returns a string representation of the stash entry. + * For defined stash entries, returns the glob representation. + * For undefined stash entries, returns undef. + * + * @return A string representation of the stash entry. + */ + @Override + public String toString() { + // For stash entries, always return the glob representation + // This matches Perl's behavior where stash entries stringify to "*PackageName::symbol" + return "*" + this.globName; + } + } diff --git a/src/test/resources/unit/getopt_long_regression.t b/src/test/resources/unit/getopt_long_regression.t index a5e7cc8e..0b5b9b32 100644 --- a/src/test/resources/unit/getopt_long_regression.t +++ b/src/test/resources/unit/getopt_long_regression.t @@ -11,7 +11,7 @@ use 5.32.0; use strict; use warnings; -use Test::More tests => 6; +use Test::More tests => 8; # Test Getopt::Long functionality that was broken by the regression BEGIN { @@ -23,20 +23,31 @@ can_ok('main', 'GetOptions'); # Test 2: Simple option parsing without arguments my $simple_flag = 0; -my @ARGV = ('--simple'); +# Save original @ARGV +my @original_argv = @ARGV; +# Set @ARGV to simulate command line arguments +@ARGV = ('--simple'); ok(GetOptions('simple' => \$simple_flag), 'Simple flag parsing works'); is($simple_flag, 1, 'Simple flag was set correctly'); +# Restore original @ARGV +@ARGV = @original_argv; # Test 3: Option with value my $with_value = ''; +# Save original @ARGV +my @original_argv3 = @ARGV; @ARGV = ('--value=test123'); ok(GetOptions('value=s' => \$with_value), 'Option with value parsing works'); is($with_value, 'test123', 'Value option was set correctly'); +# Restore original @ARGV +@ARGV = @original_argv3; # Test 4: Multiple options my $width = 10; my $height = 5; my $generations = 1; +# Save original @ARGV +my @original_argv4 = @ARGV; @ARGV = ('--width=10', '--height=5', '--generations=1'); ok(GetOptions( 'width=i' => \$width, @@ -46,3 +57,5 @@ ok(GetOptions( is_deeply([$width, $height, $generations], [10, 5, 1], 'Multiple options were set correctly'); +# Restore original @ARGV +@ARGV = @original_argv4;