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 @@ -90,6 +90,10 @@ private static class LoopInfo {
// Track current calling context for subroutine calls
int currentCallContext = RuntimeContextType.LIST; // Default to LIST

// The original caller context before eval STRING body promotion (VOID→SCALAR).
// wantarray inside eval STRING must reflect the TRUE outer context, not the promoted one.
int outerCallContext = RuntimeContextType.LIST;

// Closure support
private RuntimeBase[] capturedVars; // Captured variable values
private String[] capturedVarNames; // Parallel array of names
Expand Down Expand Up @@ -473,14 +477,11 @@ public InterpretedCode compile(Node node, EmitterContext ctx) {
// Detect closure variables if context is provided
if (ctx != null) {
detectClosureVariables(node, ctx);
// Use the calling context from EmitterContext for top-level expressions.
// Exception: eval STRING body always produces the value of its last expression,
// even when the caller uses it in void context. Compiling the body in VOID
// context would discard the result (e.g. `INIT { eval '1' or die }` would
// fail because eval returns undef). Use SCALAR as the minimum context.
currentCallContext = (ctx.contextType == RuntimeContextType.VOID)
? RuntimeContextType.SCALAR
: ctx.contextType;
// Use the calling context from EmitterContext, exactly as the JVM compiler does.
// The true outer context is passed through unchanged so wantarray inside the
// eval body reflects the real call site context.
outerCallContext = ctx.contextType;
currentCallContext = ctx.contextType;

// Sync package, pragmas, warnings, and features from ctx.symbolTable.
// ctx.symbolTable is the compile-time scope snapshot at the eval call site —
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -849,11 +849,16 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode
// exactly as the JVM compiler does via EmitEval/evalTag.
// The evalTag is unique per eval site and baked into the string pool.
String evalTag = "interp_eval_" + EVAL_TAG_COUNTER.incrementAndGet();
// currentCallContext is the TRUE outer context (e.g. VOID for a statement eval).
// This is what wantarray inside the eval body must see.
// The VOID→SCALAR promotion in BytecodeCompiler.compile() affects only how the
// eval BODY's last expression is compiled, not the runtime call context.
int outerCtx = bytecodeCompiler.currentCallContext;
EmitterContext snapCtx = new EmitterContext(
new JavaClassInfo(),
bytecodeCompiler.symbolTable.snapShot(), // compile-time scope snapshot
null, null,
bytecodeCompiler.currentCallContext,
outerCtx,
false,
null,
new CompilerOptions(),
Expand All @@ -864,7 +869,7 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode
bytecodeCompiler.emitWithToken(Opcodes.EVAL_STRING, node.getIndex());
bytecodeCompiler.emitReg(rd);
bytecodeCompiler.emitReg(stringReg);
bytecodeCompiler.emit(bytecodeCompiler.currentCallContext);
bytecodeCompiler.emit(outerCtx);
bytecodeCompiler.emit(tagIdx);

bytecodeCompiler.lastResultReg = rd;
Expand Down