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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ all: build
# CI build - optimized for CI/CD environments
ci: check-java-gradle
ifeq ($(OS),Windows_NT)
gradlew.bat classes testClasses shadowJar --no-daemon --stacktrace
gradlew.bat build --no-daemon --stacktrace
else
./gradlew build --no-daemon --stacktrace
endif
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/perlonjava/app/cli/CompilerOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class CompilerOptions implements Cloneable {
public boolean processAndPrint = false; // For -p
public boolean inPlaceEdit = false; // New field for in-place editing
public String code = null;
public String deparseSourceCode = null;
public byte[] rawCodeBytes = null; // Raw file bytes (after BOM removal) for DATA section
public boolean codeHasEncoding = false;
public String fileName = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ public static RuntimeList executePerlCode(CompilerOptions compilerOptions,
if (compilerOptions.applySourceFilters) {
compilerOptions.code = FilterUtilCall.preprocessWithBeginFilters(compilerOptions.code);
}
compilerOptions.deparseSourceCode = compilerOptions.code;

// Create the LexerToken list
Lexer lexer = new Lexer(compilerOptions.code);
Expand Down Expand Up @@ -690,6 +691,7 @@ public static Object compilePerlCode(CompilerOptions compilerOptions) throws Exc
}

// Tokenize
compilerOptions.deparseSourceCode = compilerOptions.code;
Lexer lexer = new Lexer(compilerOptions.code);
List<LexerToken> tokens = lexer.tokenize();
compilerOptions.code = null; // Free memory
Expand All @@ -709,4 +711,3 @@ public static Object compilePerlCode(CompilerOptions compilerOptions) throws Exc
return compileToExecutable(ast, ctx);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -6349,9 +6349,11 @@ public void visit(ListNode node) {
return;
}

int elementContext = currentCallContext == RuntimeContextType.LVALUE_LIST
? RuntimeContextType.LVALUE_LIST
: RuntimeContextType.LIST;
int elementContext = switch (currentCallContext) {
case RuntimeContextType.RUNTIME -> RuntimeContextType.RUNTIME;
case RuntimeContextType.LVALUE_LIST -> RuntimeContextType.LVALUE_LIST;
default -> RuntimeContextType.LIST;
};

// Fast path: single element in LIST context
// In list context, returns a RuntimeList with one element
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,10 @@ else if (node.right instanceof BinaryOperatorNode rightCall) {
// Allocate result register
int rd = bytecodeCompiler.allocateOutputRegister();

// Emit CALL_METHOD
// Use emitWithToken so pcToTokenIndex maps the call instruction to the
// invocant's token index (call-site line), not the closing ')' line.
// This ensures caller() inside the called method reports the correct line.
int callSiteToken = node.left.getIndex();
// Emit CALL_METHOD. Perl reports ordinary method calls at
// the expression start, but literal anon sub/block
// arguments report the block/arg line.
int callSiteToken = methodCallerLineCallSiteToken(node, argsNode);
if (callSiteToken > 0) {
bytecodeCompiler.emitWithToken(Opcodes.CALL_METHOD, callSiteToken);
} else {
Expand Down Expand Up @@ -418,10 +417,9 @@ else if (node.right instanceof BinaryOperatorNode rightCall) {
// Check if this is a &func (no parens) call that should share caller's @_
boolean shareCallerArgs = node.getBooleanAnnotation("shareCallerArgs");

// Emit CALL_SUB or CALL_SUB_SHARE_ARGS opcode. Ordinary named calls
// report the closing line, but non-& prototyped calls report the
// expression start; keep the interpreter mapping aligned with the
// JVM emitter.
// Emit CALL_SUB or CALL_SUB_SHARE_ARGS opcode. Perl reports the
// expression start for ordinary multi-line calls, but literal anon
// sub/block arguments and &-prototype calls report the block/arg line.
int callSiteToken = callerLineCallSiteToken(node);
int rd = CompileBinaryOperatorHelper.compileBinaryOperatorSwitch(
bytecodeCompiler, node.operator, rs1, rs2, callSiteToken,
Expand Down Expand Up @@ -781,7 +779,7 @@ private static void compileJoinBinaryOp(BytecodeCompiler bytecodeCompiler, Binar
}

private static int callerLineCallSiteToken(BinaryOperatorNode node) {
if (usesExpressionStartLine(node)) {
if (!usesBlockArgumentLine(node)) {
return expressionStartIndex(node);
}

Expand All @@ -792,27 +790,47 @@ private static int callerLineCallSiteToken(BinaryOperatorNode node) {
}

private static int expressionStartIndex(BinaryOperatorNode node) {
if (node.getIndex() > 0) {
return node.getIndex();
if (node.left != null && node.left.getIndex() > 0) {
return node.left.getIndex();
}
return node.getIndex() > 0 ? node.getIndex() : -1;
}

private static int methodCallerLineCallSiteToken(BinaryOperatorNode node, Node argsNode) {
if (firstArgumentIsLiteralSub(argsNode) && argsNode.getIndex() > 0) {
return argsNode.getIndex();
}

return node.left != null ? node.left.getIndex() : -1;
}

private static boolean usesExpressionStartLine(BinaryOperatorNode node) {
private static boolean usesBlockArgumentLine(BinaryOperatorNode node) {
String prototype = directCallPrototype(node);
if (prototype == null) {
if (prototype != null) {
for (int i = 0; i < prototype.length(); i++) {
char c = prototype.charAt(i);
if (Character.isWhitespace(c) || c == ';' || c == ',') {
continue;
}
return c == '&';
}

return false;
}

for (int i = 0; i < prototype.length(); i++) {
char c = prototype.charAt(i);
if (Character.isWhitespace(c) || c == ';' || c == ',') {
continue;
}
return c != '&';
return firstArgumentIsLiteralSub(node);
}

private static boolean firstArgumentIsLiteralSub(BinaryOperatorNode node) {
return firstArgumentIsLiteralSub(node.right);
}

private static boolean firstArgumentIsLiteralSub(Node argsNode) {
if (!(argsNode instanceof ListNode list) || list.elements == null || list.elements.isEmpty()) {
return false;
}

return true;
return list.elements.get(0) instanceof SubroutineNode;
}

private static String directCallPrototype(BinaryOperatorNode node) {
Expand Down
Loading
Loading