diff --git a/src/main/java/org/perlonjava/codegen/EmitLiteral.java b/src/main/java/org/perlonjava/codegen/EmitLiteral.java index 4ed0d6e..a27b9f4 100644 --- a/src/main/java/org/perlonjava/codegen/EmitLiteral.java +++ b/src/main/java/org/perlonjava/codegen/EmitLiteral.java @@ -129,12 +129,12 @@ static void emitList(EmitterVisitor emitterVisitor, ListNode node) { mv.visitInsn(Opcodes.DUP); // stack: [RuntimeList] [RuntimeList] - emitterVisitor.ctx.javaClassInfo.asmStackLevel += 2; + emitterVisitor.ctx.javaClassInfo.incrementStackLevel(2);; // emit the list element element.accept(emitterVisitor.with(RuntimeContextType.LIST)); - emitterVisitor.ctx.javaClassInfo.asmStackLevel -= 2; + emitterVisitor.ctx.javaClassInfo.decrementStackLevel(2);; // Call the add method to add the element to the RuntimeList // This calls RuntimeDataProvider.addToList() in order to allow (1, 2, $x, @x, %x) diff --git a/src/main/java/org/perlonjava/codegen/EmitOperator.java b/src/main/java/org/perlonjava/codegen/EmitOperator.java index 39ed686..8f3d55f 100644 --- a/src/main/java/org/perlonjava/codegen/EmitOperator.java +++ b/src/main/java/org/perlonjava/codegen/EmitOperator.java @@ -596,14 +596,10 @@ static void handleNextOperator(EmitterContext ctx, OperatorNode node) { throw new PerlCompilerException(node.tokenIndex, "Can't \"" + operator + "\" outside a loop block", ctx.errorUtil); } - ctx.logDebug("visit(next): asmStackLevel: " + ctx.javaClassInfo.asmStackLevel); + ctx.logDebug("visit(next): asmStackLevel: " + ctx.javaClassInfo.stackLevelManager.getStackLevel()); - int consumeStack = ctx.javaClassInfo.asmStackLevel; - int targetStack = loopLabels.asmStackLevel; - while (consumeStack-- > targetStack) { - // Consume the JVM stack. - ctx.mv.visitInsn(Opcodes.POP); - } + // Use StackLevelManager to emit POP instructions + ctx.javaClassInfo.stackLevelManager.emitPopInstructions(ctx.mv, loopLabels.asmStackLevel); // Determine the appropriate label to jump to. Label label = operator.equals("next") ? loopLabels.nextLabel @@ -614,19 +610,18 @@ static void handleNextOperator(EmitterContext ctx, OperatorNode node) { // Handles the 'return' operator, which exits a subroutine and returns a value. static void handleReturnOperator(EmitterVisitor emitterVisitor, OperatorNode node) { - emitterVisitor.ctx.logDebug("visit(return) in context " + emitterVisitor.ctx.contextType); - emitterVisitor.ctx.logDebug("visit(return) will visit " + node.operand + " in context " + emitterVisitor.ctx.with(RuntimeContextType.RUNTIME).contextType); + EmitterContext ctx = emitterVisitor.ctx; - int consumeStack = emitterVisitor.ctx.javaClassInfo.asmStackLevel; - while (consumeStack-- > 0) { - // Consume the JVM stack. - emitterVisitor.ctx.mv.visitInsn(Opcodes.POP); - } + ctx.logDebug("visit(return) in context " + emitterVisitor.ctx.contextType); + ctx.logDebug("visit(return) will visit " + node.operand + " in context " + emitterVisitor.ctx.with(RuntimeContextType.RUNTIME).contextType); + + // Use StackLevelManager to emit POP instructions + ctx.javaClassInfo.stackLevelManager.emitPopInstructions(ctx.mv, 0); if (node.operand instanceof ListNode list) { if (list.elements.size() == 1) { // Special case for a list with 1 element. - list.elements.get(0).accept(emitterVisitor.with(RuntimeContextType.RUNTIME)); + list.elements.getFirst().accept(emitterVisitor.with(RuntimeContextType.RUNTIME)); emitterVisitor.ctx.mv.visitJumpInsn(Opcodes.GOTO, emitterVisitor.ctx.javaClassInfo.returnLabel); return; } diff --git a/src/main/java/org/perlonjava/codegen/EmitStatement.java b/src/main/java/org/perlonjava/codegen/EmitStatement.java index e8692c4..dcfb85f 100644 --- a/src/main/java/org/perlonjava/codegen/EmitStatement.java +++ b/src/main/java/org/perlonjava/codegen/EmitStatement.java @@ -195,7 +195,7 @@ static void emitFor1(EmitterVisitor emitterVisitor, For1Node node) { mv.visitInsn(Opcodes.POP); // we don't need the variable in the stack // Stack: [iterator] - emitterVisitor.ctx.javaClassInfo.asmStackLevel += 1; + emitterVisitor.ctx.javaClassInfo.incrementStackLevel(1);; // Add redo label Label redoLabel = new Label(); @@ -226,7 +226,7 @@ static void emitFor1(EmitterVisitor emitterVisitor, For1Node node) { // End of the loop mv.visitLabel(loopEnd); - emitterVisitor.ctx.javaClassInfo.asmStackLevel -= 1; + emitterVisitor.ctx.javaClassInfo.decrementStackLevel(1);; // Pop the iterator from the stack mv.visitInsn(Opcodes.POP); diff --git a/src/main/java/org/perlonjava/codegen/JavaClassInfo.java b/src/main/java/org/perlonjava/codegen/JavaClassInfo.java index 0bc61cd..5fe9aed 100644 --- a/src/main/java/org/perlonjava/codegen/JavaClassInfo.java +++ b/src/main/java/org/perlonjava/codegen/JavaClassInfo.java @@ -5,67 +5,109 @@ import java.util.ArrayDeque; import java.util.Deque; +/** + * Represents information about a Java class being generated. + * This includes the class name, return label, stack level management, + * and a stack of loop labels for managing nested loops. + */ public class JavaClassInfo { - /** - * The name of the Java class being generated. - */ + /** The name of the Java class. */ public String javaClassName; - /** - * The label to which the current method should return. - */ + /** The label to return to after method execution. */ public Label returnLabel; - /** - * ASM stack level - */ - public int asmStackLevel; + /** Manages the stack level for the class. */ + public StackLevelManager stackLevelManager; - /** - * Stack to hold loop label information - */ + /** A stack of loop labels for managing nested loops. */ public Deque loopLabelStack; + /** + * Constructs a new JavaClassInfo object. + * Initializes the class name, stack level manager, and loop label stack. + */ public JavaClassInfo() { this.javaClassName = EmitterMethodCreator.generateClassName(); this.returnLabel = null; - this.asmStackLevel = 0; + this.stackLevelManager = new StackLevelManager(); this.loopLabelStack = new ArrayDeque<>(); } /** - * Push a new LoopLabels object onto the stack. + * Pushes a new set of loop labels onto the loop label stack. + * + * @param labelName the name of the loop label + * @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 */ public void pushLoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel) { - loopLabelStack.push(new LoopLabels(labelName, nextLabel, redoLabel, lastLabel, asmStackLevel)); + loopLabelStack.push(new LoopLabels(labelName, nextLabel, redoLabel, lastLabel, stackLevelManager.getStackLevel())); } /** - * Pop the top LoopLabels object off the stack. + * Pops the top set of loop labels from the loop label stack. */ public void popLoopLabels() { loopLabelStack.pop(); } + /** + * Finds loop labels by their name. + * + * @param labelName the name of the loop label to find + * @return the LoopLabels object with the specified name, or the top of the stack if the name is null + */ public LoopLabels findLoopLabelsByName(String labelName) { if (labelName == null) { return loopLabelStack.peek(); } for (LoopLabels loopLabels : loopLabelStack) { if (loopLabels.labelName.equals(labelName)) { - return loopLabels; // Found the matching label + return loopLabels; } } - return null; // Label not found + return null; + } + + /** + * Increments the stack level by a specified amount. + * + * @param level the amount to increment the stack level by + */ + public void incrementStackLevel(int level) { + stackLevelManager.increment(level); + } + + /** + * Decrements the stack level by a specified amount. + * + * @param level the amount to decrement the stack level by + */ + public void decrementStackLevel(int level) { + stackLevelManager.decrement(level); } + /** + * Resets the stack level to its initial state. + */ + public void resetStackLevel() { + stackLevelManager.reset(); + } + + /** + * Returns a string representation of the JavaClassInfo object. + * + * @return a string representation of the JavaClassInfo object + */ @Override public String toString() { return "JavaClassInfo{\n" + " javaClassName='" + javaClassName + "',\n" + " returnLabel=" + (returnLabel != null ? returnLabel.toString() : "null") + ",\n" + - " asmStackLevel=" + asmStackLevel + ",\n" + + " asmStackLevel=" + stackLevelManager.getStackLevel() + ",\n" + " loopLabelStack=" + loopLabelStack + "\n" + "}"; } diff --git a/src/main/java/org/perlonjava/codegen/StackLevelManager.java b/src/main/java/org/perlonjava/codegen/StackLevelManager.java new file mode 100644 index 0000000..eb43b12 --- /dev/null +++ b/src/main/java/org/perlonjava/codegen/StackLevelManager.java @@ -0,0 +1,38 @@ +package org.perlonjava.codegen; + +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class StackLevelManager { + private int stackLevel; + + public StackLevelManager() { + this.stackLevel = 0; + } + + public int getStackLevel() { + return stackLevel; + } + + public void increment(int level) { + stackLevel += level; + } + + public void decrement(int level) { + stackLevel -= level; + if (stackLevel < 0) { + stackLevel = 0; + } + } + + public void reset() { + stackLevel = 0; + } + + public void emitPopInstructions(MethodVisitor mv, int targetStackLevel) { + while (stackLevel > targetStackLevel) { + mv.visitInsn(Opcodes.POP); + decrement(1); + } + } +}