Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -493,12 +493,7 @@ public InterpretedCode compile(Node node, EmitterContext ctx) {
// Use the calling context from EmitterContext for top-level expressions
// This is crucial for eval STRING to propagate context correctly
currentCallContext = ctx.contextType;
// Inherit package from the JVM compiler context so unqualified sub calls
// resolve in the correct package (not main)
if (ctx.symbolTable != null) {
symbolTable.setCurrentPackage(ctx.symbolTable.getCurrentPackage(),
ctx.symbolTable.currentPackageIsClass());
}

}

// If we have captured variables, allocate registers for them
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,14 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c
pc = OpcodeHandlerExtended.executeLstat(bytecode, pc, registers);
break;

case Opcodes.STAT_LASTHANDLE:
pc = OpcodeHandlerExtended.executeStatLastHandle(bytecode, pc, registers);
break;

case Opcodes.LSTAT_LASTHANDLE:
pc = OpcodeHandlerExtended.executeLstatLastHandle(bytecode, pc, registers);
break;

// File test operations (opcodes 190-216) - delegated to handler
case Opcodes.FILETEST_R:
case Opcodes.FILETEST_W:
Expand Down
42 changes: 29 additions & 13 deletions src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -554,21 +554,37 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode
}
} else if (op.equals("stat") || op.equals("lstat")) {
// stat FILE or lstat FILE
int savedContext = bytecodeCompiler.currentCallContext;
bytecodeCompiler.currentCallContext = RuntimeContextType.SCALAR;
try {
node.operand.accept(bytecodeCompiler);
int operandReg = bytecodeCompiler.lastResultReg;
boolean isUnderscoreOperand = (node.operand instanceof IdentifierNode)
&& ((IdentifierNode) node.operand).name.equals("_");

int rd = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emit(op.equals("stat") ? Opcodes.STAT : Opcodes.LSTAT);
bytecodeCompiler.emitReg(rd);
bytecodeCompiler.emitReg(operandReg);
bytecodeCompiler.emit(savedContext); // Pass calling context
if (isUnderscoreOperand) {
int savedContext = bytecodeCompiler.currentCallContext;
try {
int rd = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emit(op.equals("stat") ? Opcodes.STAT_LASTHANDLE : Opcodes.LSTAT_LASTHANDLE);
bytecodeCompiler.emitReg(rd);
bytecodeCompiler.emit(savedContext);
bytecodeCompiler.lastResultReg = rd;
} finally {
bytecodeCompiler.currentCallContext = savedContext;
}
} else {
int savedContext = bytecodeCompiler.currentCallContext;
bytecodeCompiler.currentCallContext = RuntimeContextType.SCALAR;
try {
node.operand.accept(bytecodeCompiler);
int operandReg = bytecodeCompiler.lastResultReg;

bytecodeCompiler.lastResultReg = rd;
} finally {
bytecodeCompiler.currentCallContext = savedContext;
int rd = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emit(op.equals("stat") ? Opcodes.STAT : Opcodes.LSTAT);
bytecodeCompiler.emitReg(rd);
bytecodeCompiler.emitReg(operandReg);
bytecodeCompiler.emit(savedContext);

bytecodeCompiler.lastResultReg = rd;
} finally {
bytecodeCompiler.currentCallContext = savedContext;
}
}
} else if (op.startsWith("-") && op.length() == 2) {
// File test operators: -r, -w, -x, etc.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,20 @@ public static int executeLstat(int[] bytecode, int pc, RuntimeBase[] registers)
return pc;
}

public static int executeStatLastHandle(int[] bytecode, int pc, RuntimeBase[] registers) {
int rd = bytecode[pc++];
int ctx = bytecode[pc++];
registers[rd] = Stat.statLastHandle(ctx);
return pc;
}

public static int executeLstatLastHandle(int[] bytecode, int pc, RuntimeBase[] registers) {
int rd = bytecode[pc++];
int ctx = bytecode[pc++];
registers[rd] = Stat.lstatLastHandle(ctx);
return pc;
}

/**
* Execute print operation.
* Format: PRINT contentReg filehandleReg
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/org/perlonjava/backend/bytecode/Opcodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -1149,5 +1149,13 @@ public class Opcodes {
/** Defined-or assignment: rd //= rs. Format: DEFINED_OR_ASSIGN rd rs */
public static final short DEFINED_OR_ASSIGN = 349;

/** stat _ (use cached stat buffer): rd = Stat.statLastHandle()
* Format: STAT_LASTHANDLE rd ctx */
public static final short STAT_LASTHANDLE = 350;

/** lstat _ (use cached stat buffer): rd = Stat.lstatLastHandle()
* Format: LSTAT_LASTHANDLE rd ctx */
public static final short LSTAT_LASTHANDLE = 351;

private Opcodes() {} // Utility class - no instantiation
}
16 changes: 6 additions & 10 deletions src/main/java/org/perlonjava/backend/jvm/EmitOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -864,24 +864,20 @@ static void handleStatOperator(EmitterVisitor emitterVisitor, OperatorNode node,

if (node.operand instanceof IdentifierNode identNode &&
identNode.name.equals("_")) {
// stat _ or lstat _ - still use the old methods since they don't take args
// stat _ or lstat _ - use cached stat buffer with context
emitterVisitor.pushCallContext();
emitterVisitor.ctx.mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
"org/perlonjava/runtime/operators/Stat",
operator + "LastHandle",
"()Lorg/perlonjava/runtime/runtimetypes/RuntimeList;",
"(I)Lorg/perlonjava/runtime/runtimetypes/RuntimeBase;",
false);
// Handle context - treat as list that needs conversion
if (emitterVisitor.ctx.contextType == RuntimeContextType.VOID) {
handleVoidContext(emitterVisitor);
} else if (emitterVisitor.ctx.contextType == RuntimeContextType.SCALAR) {
// Convert with stat's special semantics
emitterVisitor.ctx.mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"org/perlonjava/runtime/runtimetypes/RuntimeList",
"statScalar",
"()Lorg/perlonjava/runtime/runtimetypes/RuntimeScalar;",
false);
emitterVisitor.ctx.mv.visitTypeInsn(Opcodes.CHECKCAST, "org/perlonjava/runtime/runtimetypes/RuntimeScalar");
} else if (emitterVisitor.ctx.contextType == RuntimeContextType.LIST) {
emitterVisitor.ctx.mv.visitTypeInsn(Opcodes.CHECKCAST, "org/perlonjava/runtime/runtimetypes/RuntimeList");
}
} else {
// stat EXPR or lstat EXPR - use context-aware methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1619,6 +1619,12 @@ private static InterpretedCode compileToInterpreter(
ctx.errorUtil
);

// Inherit package from the JVM compiler context so unqualified sub calls
// resolve in the correct package (not main) in the interpreter fallback.
if (ctx.symbolTable != null) {
compiler.setCompilePackage(ctx.symbolTable.getCurrentPackage());
}

// Compile AST to interpreter bytecode (pass ctx for package context and closure detection)
InterpretedCode code = compiler.compile(ast, ctx);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ private Node generateTooFewArgsMessage() {
new StringNode("Too few arguments for subroutine '" + fullName + "' (got ", parser.tokenIndex),
argCount,
parser.tokenIndex),
new StringNode("; expected at least ", parser.tokenIndex),
new StringNode(minParams == maxParams ? "; expected " : "; expected at least ", parser.tokenIndex),
parser.tokenIndex),
new NumberNode(Integer.toString(adjustedMin), parser.tokenIndex),
parser.tokenIndex),
Expand Down Expand Up @@ -539,7 +539,7 @@ private Node generateTooManyArgsMessage() {
new StringNode("Too many arguments for subroutine '" + fullName + "' (got ", parser.tokenIndex),
argCount,
parser.tokenIndex),
new StringNode("; expected at most ", parser.tokenIndex),
new StringNode(minParams == maxParams ? "; expected " : "; expected at most ", parser.tokenIndex),
parser.tokenIndex),
new NumberNode(Integer.toString(adjustedMax), parser.tokenIndex),
parser.tokenIndex),
Expand Down
35 changes: 31 additions & 4 deletions src/main/java/org/perlonjava/runtime/operators/Stat.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.perlonjava.runtime.operators.FileTestOperator.lastBasicAttr;
import static org.perlonjava.runtime.operators.FileTestOperator.lastFileHandle;
import static org.perlonjava.runtime.operators.FileTestOperator.lastPosixAttr;
import static org.perlonjava.runtime.operators.FileTestOperator.lastStatOk;
import static org.perlonjava.runtime.operators.FileTestOperator.updateLastStat;
import static org.perlonjava.runtime.runtimetypes.GlobalVariable.getGlobalVariable;
import static org.perlonjava.runtime.runtimetypes.RuntimeIO.resolvePath;
Expand Down Expand Up @@ -63,12 +64,38 @@ private static int getPermissionsOctal(BasicFileAttributes basicAttr, PosixFileA
return permissions;
}

public static RuntimeList statLastHandle() {
return stat(lastFileHandle);
public static RuntimeBase statLastHandle(int ctx) {
if (!lastStatOk) {
getGlobalVariable("main::!").set(9);
RuntimeList empty = new RuntimeList();
if (ctx == RuntimeContextType.SCALAR) {
return empty.statScalar();
}
return empty;
}
RuntimeList res = new RuntimeList();
statInternal(res, lastBasicAttr, lastPosixAttr);
if (ctx == RuntimeContextType.SCALAR) {
return res.statScalar();
}
return res;
}

public static RuntimeList lstatLastHandle() {
return lstat(lastFileHandle);
public static RuntimeBase lstatLastHandle(int ctx) {
if (!lastStatOk) {
getGlobalVariable("main::!").set(9);
RuntimeList empty = new RuntimeList();
if (ctx == RuntimeContextType.SCALAR) {
return empty.statScalar();
}
return empty;
}
RuntimeList res = new RuntimeList();
statInternal(res, lastBasicAttr, lastPosixAttr);
if (ctx == RuntimeContextType.SCALAR) {
return res.statScalar();
}
return res;
}

/**
Expand Down