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 @@ -3413,9 +3413,9 @@ void compileVariableDeclaration(OperatorNode node, String op) {
lastResultReg = rd;
return;
}
// local $hash{key} or local $array[index] - localize a hash/array element
if (node.operand instanceof BinaryOperatorNode binOp
&& (binOp.operator.equals("{") || binOp.operator.equals("["))) {
// General fallback for any lvalue expression (matches JVM backend behavior)
// Handles: local $hash{key}, local $array[index], local $obj->method->{key}, etc.
if (node.operand instanceof BinaryOperatorNode binOp) {
compileNode(binOp, -1, RuntimeContextType.SCALAR);
int elemReg = lastResultReg;
emit(Opcodes.PUSH_LOCAL_VARIABLE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ public class CompileAssignment {
private static boolean handleLocalAssignment(BytecodeCompiler bc, BinaryOperatorNode node, OperatorNode leftOp, int rhsContext) {
if (!leftOp.operator.equals("local")) return false;
Node localOperand = leftOp.operand;
if (localOperand instanceof BinaryOperatorNode hashAccess && hashAccess.operator.equals("{")) {
bc.compileNode(hashAccess, -1, rhsContext);
// General fallback for any BinaryOperatorNode lvalue (matches JVM backend behavior)
// Handles: local $hash{key} = v, local $array[i] = v, local $obj->method->{key} = v, etc.
if (localOperand instanceof BinaryOperatorNode binOp) {
bc.compileNode(binOp, -1, rhsContext);
int elemReg = bc.lastResultReg;
bc.emit(Opcodes.PUSH_LOCAL_VARIABLE);
bc.emitReg(elemReg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,13 @@ private static ArrayList<ArrayList<String>> formatThrowable(Throwable t) {
// level; consuming them in order gives the correct nested call stack.
var interpreterFrames = InterpreterState.getStack();
var interpreterPcs = InterpreterState.getPcStack();
// Start at index 0 - caller() will skip this (the current function)
int interpreterFrameIndex = 0;

// Track whether we've added a frame for the current Perl call level.
// Multiple execute() frames can occur for the same call level (for internal ops).
// InterpretedCode.apply marks the END of a call level, so we reset after seeing it.
boolean addedFrameForCurrentLevel = false;

for (var element : t.getStackTrace()) {
if (element.getClassName().equals("org.perlonjava.frontend.parser.StatementParser") &&
Expand All @@ -73,12 +79,18 @@ private static ArrayList<ArrayList<String>> formatThrowable(Throwable t) {
lastFileName = callerInfo.filename() != null ? callerInfo.filename() : "";
callerStackIndex++;
}
} else if (element.getClassName().equals("org.perlonjava.backend.bytecode.InterpretedCode") &&
element.getMethodName().equals("apply")) {
// InterpretedCode.apply marks the END of a Perl call level.
// After this, the next execute frame starts a new call level.
if (addedFrameForCurrentLevel) {
interpreterFrameIndex++;
addedFrameForCurrentLevel = false;
}
} else if (element.getClassName().equals("org.perlonjava.backend.bytecode.BytecodeInterpreter") &&
element.getMethodName().equals("execute")) {
// Consume the next interpreter frame in order.
// Using current() always returned the same topmost frame; consuming
// in order correctly maps each JVM execute() frame to its Perl level.
if (interpreterFrameIndex < interpreterFrames.size()) {
// Only add an entry for the current Perl call level once
if (!addedFrameForCurrentLevel && interpreterFrameIndex < interpreterFrames.size()) {
var frame = interpreterFrames.get(interpreterFrameIndex);
if (frame != null && frame.code() != null) {
// For the innermost frame (index 0), use the runtime current package
Expand All @@ -87,8 +99,6 @@ private static ArrayList<ArrayList<String>> formatThrowable(Throwable t) {
String pkg = (interpreterFrameIndex == 0)
? InterpreterState.currentPackage.get().toString()
: frame.packageName();
int currentInterpreterFrameIndex = interpreterFrameIndex;
interpreterFrameIndex++;

String subName = frame.subroutineName();
if (subName != null && !subName.isEmpty() && !subName.contains("::")) {
Expand All @@ -99,9 +109,9 @@ private static ArrayList<ArrayList<String>> formatThrowable(Throwable t) {
entry.add(pkg);
String filename = frame.code().sourceName;
String line = String.valueOf(frame.code().sourceLine);
if (currentInterpreterFrameIndex < interpreterPcs.size()) {
if (interpreterFrameIndex < interpreterPcs.size()) {
Integer tokenIndex = null;
int pc = interpreterPcs.get(currentInterpreterFrameIndex);
int pc = interpreterPcs.get(interpreterFrameIndex);
if (frame.code().pcToTokenIndex != null && !frame.code().pcToTokenIndex.isEmpty()) {
var entryPc = frame.code().pcToTokenIndex.floorEntry(pc);
if (entryPc != null) {
Expand All @@ -119,6 +129,7 @@ private static ArrayList<ArrayList<String>> formatThrowable(Throwable t) {
entry.add(subName);
stackTrace.add(entry);
lastFileName = filename != null ? filename : "";
addedFrameForCurrentLevel = true;
}
}
} else if (element.getClassName().contains("org.perlonjava.anon") ||
Expand Down
Loading