Skip to content

Commit fdb9a4c

Browse files
authored
Merge pull request #67 from fglock/fix/filehandle-t
Fix typeglob CODE slot access for filehandle.t
2 parents 54e3c10 + 22ad213 commit fdb9a4c

File tree

8 files changed

+108
-4
lines changed

8 files changed

+108
-4
lines changed

src/main/java/org/perlonjava/parser/CoreOperatorResolver.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ public static Node parseCoreOperator(Parser parser, LexerToken token, int startI
5959
"glob", "gmtime", "hex", "int", "lc", "lcfirst", "length", "localtime", "log",
6060
"oct", "ord", "pop", "pos", "prototype", "quotemeta", "rand", "ref", "reset",
6161
"rmdir", "shift", "sin", "sleep", "sqrt", "srand", "study", "uc",
62-
"ucfirst", "undef" -> OperatorParser.parseOperatorWithOneOptionalArgument(parser, token);
62+
"ucfirst" -> OperatorParser.parseOperatorWithOneOptionalArgument(parser, token);
63+
case "undef" -> OperatorParser.parseUndef(parser, token, currentIndex);
6364
case "select" -> OperatorParser.parseSelect(parser, token, currentIndex);
6465
case "stat", "lstat" -> OperatorParser.parseStat(parser, token, currentIndex);
6566
case "readpipe" -> OperatorParser.parseReadpipe(parser);

src/main/java/org/perlonjava/parser/OperatorParser.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,22 @@ static OperatorNode parseDefined(Parser parser, LexerToken token, int currentInd
620620
return new OperatorNode(token.text, operand, currentIndex);
621621
}
622622

623+
static OperatorNode parseUndef(Parser parser, LexerToken token, int currentIndex) {
624+
ListNode operand;
625+
// Handle 'undef' operator with special parsing context
626+
// Similar to 'defined', we need to prevent &subr from being auto-called
627+
boolean parsingTakeReference = parser.parsingTakeReference;
628+
parser.parsingTakeReference = true; // don't call `&subr` while parsing "Take reference"
629+
operand = ListParser.parseZeroOrOneList(parser, 0);
630+
parser.parsingTakeReference = parsingTakeReference;
631+
if (operand.elements.isEmpty()) {
632+
// `undef` without arguments returns undef
633+
return new OperatorNode(token.text, null, currentIndex);
634+
}
635+
636+
return new OperatorNode(token.text, operand, currentIndex);
637+
}
638+
623639
static Node parseSpecialQuoted(Parser parser, LexerToken token, int startIndex) {
624640
// Handle special-quoted domain-specific arguments
625641
String operator = token.text;

src/main/java/org/perlonjava/runtime/RuntimeGlob.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,35 @@ private void markGlobAsAssigned() {
180180
* @return A RuntimeScalar representing the dereferenced value or reference. If the key
181181
* is not recognized, an empty RuntimeScalar is returned.
182182
*/
183+
@Override
183184
public RuntimeScalar hashDerefGet(RuntimeScalar index) {
184-
// System.out.println("glob hashDerefGet " + index.toString());
185+
return getGlobSlot(index);
186+
}
187+
188+
@Override
189+
public RuntimeScalar hashDerefGetNonStrict(RuntimeScalar index, String packageName) {
190+
// For typeglobs, slot access doesn't need symbolic reference resolution
191+
// Just access the slot directly
192+
return getGlobSlot(index);
193+
}
194+
195+
/**
196+
* Get a typeglob slot (CODE, SCALAR, ARRAY, HASH, IO, FORMAT).
197+
* This is the common implementation for both strict and non-strict contexts.
198+
*/
199+
private RuntimeScalar getGlobSlot(RuntimeScalar index) {
200+
// System.out.println("glob getGlobSlot " + index.toString());
185201
return switch (index.toString()) {
186-
case "CODE" -> GlobalVariable.getGlobalCodeRef(this.globName);
202+
case "CODE" -> {
203+
// Only return CODE ref if the subroutine is actually defined
204+
RuntimeScalar codeRef = GlobalVariable.getGlobalCodeRef(this.globName);
205+
if (codeRef.type == RuntimeScalarType.CODE && codeRef.value instanceof RuntimeCode code) {
206+
if (code.defined()) {
207+
yield codeRef;
208+
}
209+
}
210+
yield new RuntimeScalar(); // Return undef if code doesn't exist
211+
}
187212
case "IO" -> IO;
188213
case "SCALAR" -> GlobalVariable.getGlobalVariable(this.globName);
189214
case "ARRAY" -> {
@@ -211,10 +236,16 @@ public RuntimeScalar getIO() {
211236

212237
public RuntimeGlob setIO(RuntimeScalar io) {
213238
this.IO = io;
239+
// If the IO scalar contains a RuntimeIO, set its glob name
240+
if (io.value instanceof RuntimeIO runtimeIO) {
241+
runtimeIO.globName = this.globName;
242+
}
214243
return this;
215244
}
216245

217246
public RuntimeGlob setIO(RuntimeIO io) {
247+
// Set the glob name in the RuntimeIO for proper stringification
248+
io.globName = this.globName;
218249
this.IO = new RuntimeScalar(io);
219250
return this;
220251
}

src/main/java/org/perlonjava/runtime/RuntimeIO.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,13 @@ protected boolean removeEldestEntry(Map.Entry<IOHandle, Boolean> eldest) {
147147
*/
148148
public DirectoryIO directoryIO;
149149

150+
/**
151+
* The name of the glob that owns this IO handle (e.g., "main::STDOUT").
152+
* Used for stringification when the filehandle is used in string context.
153+
* Null if this handle is not associated with a named glob.
154+
*/
155+
public String globName;
156+
150157
/**
151158
* Flag indicating if this handle has unflushed output.
152159
* Used to determine when automatic flushing is needed.
@@ -757,11 +764,14 @@ private Set<StandardOpenOption> convertMode(String mode) {
757764

758765
/**
759766
* Returns a string representation of this I/O handle.
760-
* Format: GLOB(0xHASHCODE)
767+
* Format: globName if known (e.g., "main::STDOUT"), otherwise GLOB(0xHASHCODE)
761768
*
762769
* @return string representation
763770
*/
764771
public String toString() {
772+
if (globName != null) {
773+
return globName;
774+
}
765775
return "GLOB(0x" + this.hashCode() + ")";
766776
}
767777

src/main/java/org/perlonjava/runtime/RuntimeScalar.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,16 @@ public RuntimeScalar createReference() {
10901090
}
10911091

10921092
public RuntimeScalar undefine() {
1093+
// Special handling for CODE type - don't set the ref to undef,
1094+
// just clear the code from the global symbol table
1095+
if (type == RuntimeScalarType.CODE && value instanceof RuntimeCode) {
1096+
// Clear the code value but keep the type as CODE
1097+
this.value = new RuntimeCode(null, null);
1098+
// Invalidate the method resolution cache
1099+
org.perlonjava.mro.InheritanceResolver.invalidateCache();
1100+
return this;
1101+
}
1102+
// For all other types, set to undef
10931103
this.type = UNDEF;
10941104
this.value = null;
10951105
return this;

src/main/perl/lib/IO/File.pm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,14 @@ sub new {
156156
$fh;
157157
}
158158

159+
sub new_tmpfile {
160+
my $class = shift;
161+
@_ == 0 or croak "usage: $class->new_tmpfile()";
162+
# TODO: Implement actual temporary file creation
163+
# For now, return undef to indicate not supported
164+
return undef;
165+
}
166+
159167
################################################
160168
## Open
161169
##

src/main/perl/lib/IO/Handle.pm

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,21 @@ sub getlines {
309309
<$fh>;
310310
}
311311

312+
sub gets {
313+
my $fh = shift;
314+
scalar <$fh>;
315+
}
316+
317+
sub _open_mode_string {
318+
my ($mode) = @_;
319+
$mode =~ /^\+?(<|>>?)$/
320+
or $mode =~ s/^r(\+?)$/$1</
321+
or $mode =~ s/^w(\+?)$/$1>/
322+
or $mode =~ s/^a(\+?)$/$1>>/
323+
or croak "IO::Handle: bad open mode: $mode";
324+
$mode;
325+
}
326+
312327
sub write {
313328
my $fh = shift;
314329
my $buf = shift;

src/main/perl/lib/IO/Seekable.pm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,17 @@ sub tell {
123123
tell($_[0]);
124124
}
125125

126+
sub getpos {
127+
@_ == 1 or croak 'usage: $io->getpos()';
128+
my $fh = $_[0];
129+
my $pos = tell($fh);
130+
return undef if $pos < 0;
131+
return $pos;
132+
}
133+
134+
sub setpos {
135+
@_ == 2 or croak 'usage: $io->setpos(POS)';
136+
seek($_[0], $_[1], 0);
137+
}
138+
126139
1;

0 commit comments

Comments
 (0)