Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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("::")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
23 changes: 22 additions & 1 deletion src/main/java/org/perlonjava/runtime/RuntimeStash.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/perlonjava/runtime/RuntimeStashEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

}
17 changes: 15 additions & 2 deletions src/test/resources/unit/getopt_long_regression.t
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -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;