diff --git a/src/main/java/org/perlonjava/codegen/EmitBlock.java b/src/main/java/org/perlonjava/codegen/EmitBlock.java index e0238cdad..2208bee61 100644 --- a/src/main/java/org/perlonjava/codegen/EmitBlock.java +++ b/src/main/java/org/perlonjava/codegen/EmitBlock.java @@ -144,7 +144,6 @@ public static void emitBlock(EmitterVisitor emitterVisitor, BlockNode node) { nextLabel, redoLabel, nextLabel, - emitterVisitor.ctx.javaClassInfo.stackLevelManager.getStackLevel(), emitterVisitor.ctx.contextType, isBareBlock, isBareBlock); diff --git a/src/main/java/org/perlonjava/codegen/EmitControlFlow.java b/src/main/java/org/perlonjava/codegen/EmitControlFlow.java index e878bd1e2..f7e506e09 100644 --- a/src/main/java/org/perlonjava/codegen/EmitControlFlow.java +++ b/src/main/java/org/perlonjava/codegen/EmitControlFlow.java @@ -107,12 +107,6 @@ static void handleNextOperator(EmitterContext ctx, OperatorNode node) { return; } - // Local control flow: use fast GOTO (existing code) - ctx.logDebug("visit(next): asmStackLevel: " + ctx.javaClassInfo.stackLevelManager.getStackLevel()); - - // Clean up the stack before jumping by popping values up to the loop's stack level - ctx.javaClassInfo.resetStackLevel(); - // Handle return values based on context if (loopLabels.context != RuntimeContextType.VOID) { if (operator.equals("next") || operator.equals("last")) { @@ -167,9 +161,6 @@ static void handleReturnOperator(EmitterVisitor emitterVisitor, OperatorNode nod } } - // Clean up tracked stack before return - ctx.javaClassInfo.resetStackLevel(); - boolean hasOperand = !(node.operand == null || (node.operand instanceof ListNode list && list.elements.isEmpty())); if (!hasOperand) { @@ -204,9 +195,6 @@ static void handleGotoSubroutine(EmitterVisitor emitterVisitor, OperatorNode sub ctx.logDebug("visit(goto &sub): Emitting TAILCALL marker"); - // Clean up tracked stack before creating the marker - ctx.javaClassInfo.resetStackLevel(); - subNode.accept(emitterVisitor.with(RuntimeContextType.SCALAR)); int codeRefSlot = ctx.javaClassInfo.acquireSpillSlot(); boolean pooledCodeRef = codeRefSlot >= 0; @@ -332,8 +320,6 @@ static void handleGotoLabel(EmitterVisitor emitterVisitor, OperatorNode node) { "(Lorg/perlonjava/runtime/RuntimeScalar;Lorg/perlonjava/runtime/RuntimeArray;Ljava/lang/String;I)V", false); - ctx.javaClassInfo.resetStackLevel(); // Clean up stack before jumping - if (pooledTarget) { ctx.javaClassInfo.releaseSpillSlot(); } @@ -388,8 +374,7 @@ static void handleGotoLabel(EmitterVisitor emitterVisitor, OperatorNode node) { } ctx.mv.visitVarInsn(Opcodes.ASTORE, markerSlot); - // Clean stack and jump to returnLabel with the marker on stack. - ctx.javaClassInfo.resetStackLevel(); + // Jump to returnLabel with the marker on stack. ctx.mv.visitVarInsn(Opcodes.ALOAD, markerSlot); if (pooledMarker) { ctx.javaClassInfo.releaseSpillSlot(); @@ -437,8 +422,7 @@ static void handleGotoLabel(EmitterVisitor emitterVisitor, OperatorNode node) { } ctx.mv.visitVarInsn(Opcodes.ASTORE, markerSlot); - // Clean stack and jump to returnLabel with the marker on stack. - ctx.javaClassInfo.resetStackLevel(); + // Jump to returnLabel with the marker on stack. ctx.mv.visitVarInsn(Opcodes.ALOAD, markerSlot); if (pooledMarker) { ctx.javaClassInfo.releaseSpillSlot(); @@ -449,8 +433,6 @@ static void handleGotoLabel(EmitterVisitor emitterVisitor, OperatorNode node) { } // Local goto: use fast GOTO (existing code) - // Clean up stack before jumping to maintain stack consistency - ctx.javaClassInfo.resetStackLevel(); // Emit the goto instruction ctx.mv.visitJumpInsn(Opcodes.GOTO, targetLabel.gotoLabel); diff --git a/src/main/java/org/perlonjava/codegen/EmitForeach.java b/src/main/java/org/perlonjava/codegen/EmitForeach.java index 28b25874b..128c52d8a 100644 --- a/src/main/java/org/perlonjava/codegen/EmitForeach.java +++ b/src/main/java/org/perlonjava/codegen/EmitForeach.java @@ -302,7 +302,6 @@ public static void emitFor1(EmitterVisitor emitterVisitor, For1Node node) { continueLabel, redoLabel, loopEnd, - emitterVisitor.ctx.javaClassInfo.stackLevelManager.getStackLevel(), RuntimeContextType.VOID); currentLoopLabels.controlFlowHandler = controlFlowHandler; diff --git a/src/main/java/org/perlonjava/codegen/EmitLogicalOperator.java b/src/main/java/org/perlonjava/codegen/EmitLogicalOperator.java index 516c1ad47..ed6bd473c 100644 --- a/src/main/java/org/perlonjava/codegen/EmitLogicalOperator.java +++ b/src/main/java/org/perlonjava/codegen/EmitLogicalOperator.java @@ -281,17 +281,10 @@ private static void emitLogicalOperatorSimple(EmitterVisitor emitterVisitor, Bin mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeBase", getBoolean, "()Z", false); mv.visitJumpInsn(compareOpcode, endLabel); - // The condition value has been consumed by getBoolean() and the conditional jump. - // Keep StackLevelManager in sync with the actual operand stack (empty) so that - // downstream non-local control flow (return/last/next/redo/goto) doesn't emit POPs - // based on stale stack accounting. - emitterVisitor.ctx.javaClassInfo.resetStackLevel(); - node.right.accept(emitterVisitor.with(RuntimeContextType.SCALAR)); mv.visitInsn(Opcodes.POP); mv.visitLabel(endLabel); - emitterVisitor.ctx.javaClassInfo.resetStackLevel(); return; } diff --git a/src/main/java/org/perlonjava/codegen/EmitStatement.java b/src/main/java/org/perlonjava/codegen/EmitStatement.java index 0b53172aa..baa5e65f1 100644 --- a/src/main/java/org/perlonjava/codegen/EmitStatement.java +++ b/src/main/java/org/perlonjava/codegen/EmitStatement.java @@ -164,7 +164,6 @@ public static void emitFor3(EmitterVisitor emitterVisitor, For3Node node) { continueLabel, redoLabel, endLabel, - emitterVisitor.ctx.javaClassInfo.stackLevelManager.getStackLevel(), RuntimeContextType.VOID, true, isUnlabeledTarget); @@ -245,7 +244,6 @@ static void emitDoWhile(EmitterVisitor emitterVisitor, For3Node node) { continueLabel, redoLabel, endLabel, - emitterVisitor.ctx.javaClassInfo.stackLevelManager.getStackLevel(), RuntimeContextType.VOID, false); // isTrueLoop = false (do-while is not a true loop) @@ -266,7 +264,6 @@ static void emitDoWhile(EmitterVisitor emitterVisitor, For3Node node) { continueLabel, redoLabel, endLabel, - emitterVisitor.ctx.javaClassInfo.stackLevelManager.getStackLevel(), RuntimeContextType.VOID, false); emitRegistryCheck(mv, loopLabels, redoLabel, continueLabel, endLabel); diff --git a/src/main/java/org/perlonjava/codegen/EmitSubroutine.java b/src/main/java/org/perlonjava/codegen/EmitSubroutine.java index 1476ba75f..a9d160072 100644 --- a/src/main/java/org/perlonjava/codegen/EmitSubroutine.java +++ b/src/main/java/org/perlonjava/codegen/EmitSubroutine.java @@ -379,8 +379,7 @@ static void handleApplyOperator(EmitterVisitor emitterVisitor, BinaryOperatorNod // If RuntimeCode.apply() returned a RuntimeControlFlowList marker, handle it here. if (ENABLE_CONTROL_FLOW_CHECKS && emitterVisitor.ctx.javaClassInfo.returnLabel != null - && emitterVisitor.ctx.javaClassInfo.controlFlowTempSlot >= 0 - && emitterVisitor.ctx.javaClassInfo.stackLevelManager.getStackLevel() <= 1) { + && emitterVisitor.ctx.javaClassInfo.controlFlowTempSlot >= 0) { Label notControlFlow = new Label(); Label propagateToCaller = new Label(); @@ -399,11 +398,6 @@ static void handleApplyOperator(EmitterVisitor emitterVisitor, BinaryOperatorNod emitterVisitor.ctx.javaClassInfo.storeSpillRef(mv, baseSpills[i]); } - // We just removed the entire base stack from the JVM operand stack via ASTORE. - // Keep StackLevelManager in sync; otherwise later emitPopInstructions() may POP the wrong values - // (including control-flow markers), producing invalid stackmap frames. - emitterVisitor.ctx.javaClassInfo.resetStackLevel(); - // Load and check if it's a control flow marker mv.visitVarInsn(Opcodes.ALOAD, emitterVisitor.ctx.javaClassInfo.controlFlowTempSlot); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, @@ -463,7 +457,6 @@ static void handleApplyOperator(EmitterVisitor emitterVisitor, BinaryOperatorNod if (loopLabels.lastLabel == emitterVisitor.ctx.javaClassInfo.returnLabel) { mv.visitJumpInsn(Opcodes.GOTO, propagateToCaller); } else { - emitterVisitor.ctx.javaClassInfo.stackLevelManager.emitPopInstructions(mv, loopLabels.asmStackLevel); if (loopLabels.context != RuntimeContextType.VOID) { EmitOperator.emitUndef(mv); } @@ -478,7 +471,6 @@ static void handleApplyOperator(EmitterVisitor emitterVisitor, BinaryOperatorNod if (loopLabels.nextLabel == emitterVisitor.ctx.javaClassInfo.returnLabel) { mv.visitJumpInsn(Opcodes.GOTO, propagateToCaller); } else { - emitterVisitor.ctx.javaClassInfo.stackLevelManager.emitPopInstructions(mv, loopLabels.asmStackLevel); if (loopLabels.context != RuntimeContextType.VOID) { EmitOperator.emitUndef(mv); } @@ -490,7 +482,6 @@ static void handleApplyOperator(EmitterVisitor emitterVisitor, BinaryOperatorNod if (loopLabels.redoLabel == emitterVisitor.ctx.javaClassInfo.returnLabel) { mv.visitJumpInsn(Opcodes.GOTO, propagateToCaller); } else { - emitterVisitor.ctx.javaClassInfo.stackLevelManager.emitPopInstructions(mv, loopLabels.asmStackLevel); mv.visitJumpInsn(Opcodes.GOTO, loopLabels.redoLabel); } diff --git a/src/main/java/org/perlonjava/codegen/GotoLabels.java b/src/main/java/org/perlonjava/codegen/GotoLabels.java index 259994da6..cec6a1f82 100644 --- a/src/main/java/org/perlonjava/codegen/GotoLabels.java +++ b/src/main/java/org/perlonjava/codegen/GotoLabels.java @@ -17,22 +17,15 @@ public class GotoLabels { */ public Label gotoLabel; - /** - * The stack level at the point where this label is defined - */ - public int asmStackLevel; - /** * Creates a new GotoLabels instance. * * @param labelName The name of the label in source code * @param gotoLabel The ASM Label object for bytecode generation - * @param asmStackLevel The stack level at label definition */ - public GotoLabels(String labelName, Label gotoLabel, int asmStackLevel) { + public GotoLabels(String labelName, Label gotoLabel) { this.labelName = labelName; this.gotoLabel = gotoLabel; - this.asmStackLevel = asmStackLevel; } /** @@ -46,7 +39,6 @@ public String toString() { return "GotoLabels{" + "labelName='" + labelName + '\'' + ", gotoLabel=" + gotoLabel + - ", asmStackLevel=" + asmStackLevel + '}'; } } diff --git a/src/main/java/org/perlonjava/codegen/JavaClassInfo.java b/src/main/java/org/perlonjava/codegen/JavaClassInfo.java index a1fe0b723..fcd49c908 100644 --- a/src/main/java/org/perlonjava/codegen/JavaClassInfo.java +++ b/src/main/java/org/perlonjava/codegen/JavaClassInfo.java @@ -64,11 +64,6 @@ public SpillRef(int slot, boolean pooled) { } } - /** - * Manages the stack level for the class. - */ - public StackLevelManager stackLevelManager; - /** * A stack of loop labels for managing nested loops. */ @@ -84,7 +79,6 @@ public JavaClassInfo() { this.javaClassName = EmitterMethodCreator.generateClassName(); this.returnLabel = null; this.returnValueSlot = -1; - this.stackLevelManager = new StackLevelManager(); this.loopLabelStack = new ArrayDeque<>(); this.gotoLabelStack = new ArrayDeque<>(); this.spillSlots = new int[0]; @@ -143,7 +137,7 @@ public void releaseSpillRef(SpillRef ref) { * @param lastLabel the label for exiting the loop */ public void pushLoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel, int context) { - loopLabelStack.push(new LoopLabels(labelName, nextLabel, redoLabel, lastLabel, stackLevelManager.getStackLevel(), context)); + loopLabelStack.push(new LoopLabels(labelName, nextLabel, redoLabel, lastLabel, context)); } /** @@ -153,16 +147,15 @@ public void pushLoopLabels(String labelName, Label nextLabel, Label redoLabel, L * @param nextLabel the label for the next iteration * @param redoLabel the label for redoing the current iteration * @param lastLabel the label for exiting the loop - * @param stackLevel the current stack level * @param context the context type * @param isTrueLoop whether this is a true loop (for/while/until) or pseudo-loop (do-while/bare) */ - public void pushLoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel, int stackLevel, int context, boolean isTrueLoop) { - loopLabelStack.push(new LoopLabels(labelName, nextLabel, redoLabel, lastLabel, stackLevel, context, isTrueLoop)); + public void pushLoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel, int context, boolean isTrueLoop) { + loopLabelStack.push(new LoopLabels(labelName, nextLabel, redoLabel, lastLabel, context, isTrueLoop)); } - public void pushLoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel, int stackLevel, int context, boolean isTrueLoop, boolean isUnlabeledControlFlowTarget) { - loopLabelStack.push(new LoopLabels(labelName, nextLabel, redoLabel, lastLabel, stackLevel, context, isTrueLoop, isUnlabeledControlFlowTarget)); + public void pushLoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel, int context, boolean isTrueLoop, boolean isUnlabeledControlFlowTarget) { + loopLabelStack.push(new LoopLabels(labelName, nextLabel, redoLabel, lastLabel, context, isTrueLoop, isUnlabeledControlFlowTarget)); } /** @@ -242,7 +235,7 @@ public LoopLabels findInnermostTrueLoopLabels() { } public void pushGotoLabels(String labelName, Label gotoLabel) { - gotoLabelStack.push(new GotoLabels(labelName, gotoLabel, stackLevelManager.getStackLevel())); + gotoLabelStack.push(new GotoLabels(labelName, gotoLabel)); } public GotoLabels findGotoLabelsByName(String labelName) { @@ -258,13 +251,6 @@ public void popGotoLabels() { gotoLabelStack.pop(); } - /** - * Resets the stack level to its initial state. - */ - public void resetStackLevel() { - stackLevelManager.reset(); - } - /** * Returns a string representation of the JavaClassInfo object. * @@ -275,7 +261,6 @@ public String toString() { return "JavaClassInfo{\n" + " javaClassName='" + javaClassName + "',\n" + " returnLabel=" + (returnLabel != null ? returnLabel.toString() : "null") + ",\n" + - " asmStackLevel=" + stackLevelManager.getStackLevel() + ",\n" + " loopLabelStack=" + loopLabelStack + "\n" + " gotoLabelStack=" + gotoLabelStack + "\n" + "}"; diff --git a/src/main/java/org/perlonjava/codegen/LoopLabels.java b/src/main/java/org/perlonjava/codegen/LoopLabels.java index 878248275..63e1f62bb 100644 --- a/src/main/java/org/perlonjava/codegen/LoopLabels.java +++ b/src/main/java/org/perlonjava/codegen/LoopLabels.java @@ -40,11 +40,6 @@ public class LoopLabels { */ public int context; - /** - * The stack level at the point where these loop labels are defined - */ - public int asmStackLevel; - /** * Whether this is a "true" loop (for/while/until) vs a pseudo-loop (do-while/bare block). * True loops allow last/next/redo. Pseudo-loops cause compile errors. @@ -70,11 +65,10 @@ public class LoopLabels { * @param nextLabel The ASM Label for 'next' operations * @param redoLabel The ASM Label for 'redo' operations * @param lastLabel The ASM Label for 'last' operations - * @param asmStackLevel The stack level at label definition * @param context The context type for this loop */ - public LoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel, int asmStackLevel, int context) { - this(labelName, nextLabel, redoLabel, lastLabel, asmStackLevel, context, true, true); + public LoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel, int context) { + this(labelName, nextLabel, redoLabel, lastLabel, context, true, true); } /** @@ -84,20 +78,18 @@ public LoopLabels(String labelName, Label nextLabel, Label redoLabel, Label last * @param nextLabel The ASM Label for 'next' operations * @param redoLabel The ASM Label for 'redo' operations * @param lastLabel The ASM Label for 'last' operations - * @param asmStackLevel The stack level at label definition * @param context The context type for this loop * @param isTrueLoop Whether this is a true loop (for/while/until) or pseudo-loop (do-while/bare) */ - public LoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel, int asmStackLevel, int context, boolean isTrueLoop) { - this(labelName, nextLabel, redoLabel, lastLabel, asmStackLevel, context, isTrueLoop, true); + public LoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel, int context, boolean isTrueLoop) { + this(labelName, nextLabel, redoLabel, lastLabel, context, isTrueLoop, true); } - public LoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel, int asmStackLevel, int context, boolean isTrueLoop, boolean isUnlabeledControlFlowTarget) { + public LoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel, int context, boolean isTrueLoop, boolean isUnlabeledControlFlowTarget) { this.labelName = labelName; this.nextLabel = nextLabel; this.redoLabel = redoLabel; this.lastLabel = lastLabel; - this.asmStackLevel = asmStackLevel; this.context = context; this.isTrueLoop = isTrueLoop; this.isUnlabeledControlFlowTarget = isUnlabeledControlFlowTarget; @@ -116,7 +108,6 @@ public String toString() { ", nextLabel=" + nextLabel + ", redoLabel=" + redoLabel + ", lastLabel=" + lastLabel + - ", asmStackLevel=" + asmStackLevel + ", context=" + context + '}'; } diff --git a/src/main/java/org/perlonjava/codegen/StackLevelManager.java b/src/main/java/org/perlonjava/codegen/StackLevelManager.java deleted file mode 100644 index c0536bf85..000000000 --- a/src/main/java/org/perlonjava/codegen/StackLevelManager.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.perlonjava.codegen; - -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - -/** - * The StackLevelManager class manages the stack level during bytecode generation. - * It tracks the current stack level and provides methods to adjust it, ensuring - * the stack is balanced, especially when using control flow instructions like GOTO. - */ -public class StackLevelManager { - // The current stack level. - private int stackLevel; - - /** - * Constructs a new StackLevelManager with an initial stack level of zero. - */ - public StackLevelManager() { - this.stackLevel = 0; - } - - /** - * Returns the current stack level. - * - * @return the current stack level. - */ - public int getStackLevel() { - return stackLevel; - } - - /** - * Resets the stack level to zero. - */ - public void reset() { - stackLevel = 0; - } - - /** - * Emits the necessary POP instructions to adjust the stack to the target - * stack level. This ensures the stack is balanced when control flow - * instructions like GOTO are executed. - * - * @param mv the MethodVisitor used to emit the bytecode instructions. - * @param targetStackLevel the desired stack level to adjust to. - */ - public void emitPopInstructions(MethodVisitor mv, int targetStackLevel) { - int current = this.stackLevel; - if (current <= targetStackLevel) { - return; - } - - int s = current; - while (s-- > targetStackLevel) { - mv.visitInsn(Opcodes.POP); - } - - // Keep the tracked stack level consistent with the actual JVM operand stack. - // If we emitted POPs, the stack height is now the target level. - this.stackLevel = targetStackLevel; - } -}