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
39 changes: 39 additions & 0 deletions cpan_smoke_20260331_135137.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Test::Deep FAIL 1266 1268 pure-perl
Try::Tiny FAIL 91 94 pure-perl
Test::Fatal PASS 19 19 pure-perl
MIME::Base32 PASS 31 31 pure-perl
HTML::Tagset PASS 33 33 pure-perl
Test::Warn FAIL 0 14 pure-perl
Path::Tiny FAIL 1488 1542 pure-perl
namespace::clean CONFIG_FAIL pure-perl
Parse::RecDescent FAIL pure-perl
Spreadsheet::WriteExcel FAIL pure-perl
Image::ExifTool FAIL pure-perl
DateTime FAIL java-xs
Spreadsheet::ParseExcel FAIL java-xs
IO::Stringy FAIL pure-perl
Moo FAIL xs-with-pp-fallback
MIME::Base64 FAIL java-xs
URI FAIL pure-perl
IO::HTML FAIL pure-perl
LWP::MediaTypes FAIL pure-perl
Test::Needs FAIL pure-perl
Test::Warnings FAIL pure-perl
Encode::Locale FAIL pure-perl
Log::Log4perl FAIL 623 624 pure-perl
JSON FAIL 23683 24886 pure-perl
Type::Tiny FAIL 18 20 pure-perl
List::MoreUtils INSTALLED xs-with-pp-fallback
Template FAIL xs-with-pp-fallback
Mojolicious FAIL pure-perl
Devel::Cover FAIL xs-required
HTTP::Message FAIL pure-perl
HTML::Parser FAIL xs-required
IO::Compress::Gzip FAIL xs-required
Moose FAIL xs-required
Plack FAIL pure-perl
LWP::UserAgent FAIL pure-perl
DBIx::Class FAIL pure-perl
DBI FAIL xs-required
Params::Util FAIL xs-with-pp-fallback
Class::Load FAIL xs-with-pp-fallback
39 changes: 39 additions & 0 deletions cpan_smoke_20260331_142811.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Test::Deep FAIL 1266 1268 pure-perl
Try::Tiny FAIL 91 94 pure-perl
Test::Fatal PASS 19 19 pure-perl
MIME::Base32 PASS 31 31 pure-perl
HTML::Tagset PASS 33 33 pure-perl
Test::Warn FAIL 0 14 pure-perl
Path::Tiny FAIL 1488 1542 pure-perl
namespace::clean FAIL 0 44 pure-perl
Parse::RecDescent FAIL 2 64 pure-perl
Spreadsheet::WriteExcel FAIL 1124 1189 pure-perl
Image::ExifTool PASS 600 600 pure-perl
DateTime FAIL 5 8 java-xs
Spreadsheet::ParseExcel PASS 1612 1612 java-xs
IO::Stringy PASS 127 127 pure-perl
Moo FAIL 809 840 xs-with-pp-fallback
MIME::Base64 FAIL 315 348 java-xs
URI FAIL 844 947 pure-perl
IO::HTML FAIL 0 52 pure-perl
LWP::MediaTypes FAIL 41 47 pure-perl
Test::Needs PASS 227 227 pure-perl
Test::Warnings FAIL 86 88 pure-perl
Encode::Locale FAIL 0 11 pure-perl
Log::Log4perl FAIL 715 719 pure-perl
JSON FAIL 23683 24886 pure-perl
Type::Tiny FAIL 18 20 pure-perl
List::MoreUtils INSTALLED xs-with-pp-fallback
Template FAIL 170 2072 xs-with-pp-fallback
Mojolicious TIMEOUT pure-perl
Devel::Cover PASS 1 1 xs-required
HTTP::Message PASS 0 0 pure-perl
HTML::Parser FAIL 190 415 xs-required
IO::Compress::Gzip FAIL 0 847 xs-required
Moose CONFIG_FAIL xs-required
Plack TIMEOUT pure-perl
LWP::UserAgent TIMEOUT pure-perl
DBIx::Class CONFIG_FAIL pure-perl
DBI FAIL 0 490 xs-required
Params::Util INSTALLED xs-with-pp-fallback
Class::Load FAIL 69 86 xs-with-pp-fallback
179 changes: 153 additions & 26 deletions dev/modules/dbix_class.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.perlonjava.backend.jvm.CompiledCode;
import org.perlonjava.backend.jvm.EmitterContext;
import org.perlonjava.backend.jvm.EmitterMethodCreator;
import org.perlonjava.backend.jvm.InterpreterFallbackException;
import org.perlonjava.backend.jvm.JavaClassInfo;
import org.perlonjava.frontend.analysis.ConstantFoldingVisitor;
import org.perlonjava.frontend.astnode.Node;
Expand Down Expand Up @@ -433,6 +434,15 @@ private static RuntimeCode compileToExecutable(Node ast, EmitterContext ctx) thr
);
return compiled;

} catch (InterpreterFallbackException fallback) {
// getBytecode() already compiled interpreter code as fallback
// when ASM frame computation failed (e.g., high fan-in to shared labels).
// Use the pre-compiled interpreter code directly.
boolean showFallback = System.getenv("JPERL_SHOW_FALLBACK") != null;
if (showFallback) {
System.err.println("Note: Using interpreter fallback (ASM frame compute crash).");
}
return fallback.interpretedCode;
} catch (Throwable e) {
// Check if this is a recoverable compilation error that can use interpreter fallback
// Catch Throwable (not just RuntimeException) because ClassFormatError
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/perlonjava/core/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public final class Configuration {
* Automatically populated by Gradle/Maven during build.
* DO NOT EDIT MANUALLY - this value is replaced at build time.
*/
public static final String gitCommitId = "509cc4f94";
public static final String gitCommitId = "3368551e7";

/**
* Git commit date of the build (ISO format: YYYY-MM-DD).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,21 @@ public static Node parseCoreOperator(Parser parser, LexerToken token, int startI
return switch (operatorName) {
case "__LINE__" -> {
handleEmptyParentheses(parser);
yield new NumberNode(Integer.toString(parser.ctx.errorUtil.getLineNumber(parser.tokenIndex)), parser.tokenIndex);
int lineNumber;
if (parser.baseLineNumber > 0) {
// Inside string interpolation sub-parser: count newlines in inner tokens
// up to current position and add to base line of the enclosing string
int newlineCount = 0;
for (int i = 0; i < parser.tokenIndex && i < parser.tokens.size(); i++) {
if (parser.tokens.get(i).type == LexerTokenType.NEWLINE) {
newlineCount++;
}
}
lineNumber = parser.baseLineNumber + newlineCount;
} else {
lineNumber = parser.ctx.errorUtil.getLineNumber(parser.tokenIndex);
}
yield new NumberNode(Integer.toString(lineNumber), parser.tokenIndex);
}
case "__FILE__" -> {
handleEmptyParentheses(parser);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/org/perlonjava/frontend/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ public class Parser {
public int heredocSkipToIndex = -1;
// The specific NEWLINE token index that should trigger the skip.
public int heredocNewlineIndex = -1;
// Base line number for string sub-parsers. When > 0, this parser operates on
// re-tokenized string content and __LINE__ should use this as the base line,
// counting newlines from the inner token list to offset from it.
public int baseLineNumber = 0;

/**
* Constructs a Parser with the given context and tokens.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ static Node parseDoubleQuotedString(EmitterContext ctx, StringParser.ParsedStrin
// Copy any other relevant context flags as needed
}

// Set base line number so __LINE__ inside @{[...]} interpolation
// returns the correct line from the original source, not the inner token list.
// Use rawStr.index (position of opening delimiter in outer token list).
parser.baseLineNumber = ctx.errorUtil.getLineNumberAccurate(rawStr.index);

// Create and run the double-quoted string parser with original token offset tracking
var doubleQuotedParser = new StringDoubleQuoted(ctx, tokens, parser, tokenIndex, isRegex, parseEscapes, interpolateVariable, isRegexReplacement);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.perlonjava.frontend.semantic.SymbolTable;
import org.perlonjava.runtime.debugger.DebugState;
import org.perlonjava.runtime.mro.InheritanceResolver;
import org.perlonjava.runtime.perlmodule.Universal;
import org.perlonjava.runtime.perlmodule.Warnings;
import org.perlonjava.runtime.runtimetypes.*;

Expand Down Expand Up @@ -790,7 +791,8 @@ public static ListNode handleNamedSubWithFilter(Parser parser, String subName, S
String msg = "Constant subroutine " + subName + " redefined" + location;
org.perlonjava.runtime.operators.WarnDie.warn(
new RuntimeScalar(msg), new RuntimeScalar(""));
} else if (dollarW || Warnings.warningManager.isWarningEnabled("redefine")) {
} else if (!Warnings.warningManager.isWarningDisabled("redefine")
&& (dollarW || Warnings.warningManager.isWarningEnabled("redefine"))) {
String msg = "Subroutine " + subName + " redefined" + location;
org.perlonjava.runtime.operators.WarnDie.warn(
new RuntimeScalar(msg), new RuntimeScalar(""));
Expand All @@ -817,6 +819,12 @@ public static ListNode handleNamedSubWithFilter(Parser parser, String subName, S
placeholder.subName = subName;
placeholder.packageName = parser.ctx.symbolTable.getCurrentPackage();

// Call MODIFY_CODE_ATTRIBUTES if attributes are present
// In Perl, this is called at compile time after the sub is defined
if (attributes != null && !attributes.isEmpty()) {
callModifyCodeAttributes(packageToUse, codeRef, attributes, parser);
}

// Optimization - https://github.com/fglock/PerlOnJava/issues/8
// Prepare capture variables
Map<Integer, SymbolTable.SymbolEntry> outerVars = parser.ctx.symbolTable.getAllVisibleVariables();
Expand Down Expand Up @@ -1028,6 +1036,57 @@ public static ListNode handleNamedSubWithFilter(Parser parser, String subName, S
return result;
}

/**
* Call MODIFY_CODE_ATTRIBUTES on the package if it exists.
* In Perl, when a subroutine is defined with attributes (sub foo : Attr { }),
* the package's MODIFY_CODE_ATTRIBUTES method is called at compile time with
* ($package, \&code, @attributes). If it returns any values, those are
* unrecognized attributes and an error is thrown.
*/
private static void callModifyCodeAttributes(String packageName, RuntimeScalar codeRef,
List<String> attributes, Parser parser) {
// Check if the package has MODIFY_CODE_ATTRIBUTES
RuntimeArray canArgs = new RuntimeArray();
RuntimeArray.push(canArgs, new RuntimeScalar(packageName));
RuntimeArray.push(canArgs, new RuntimeScalar("MODIFY_CODE_ATTRIBUTES"));

InheritanceResolver.autoloadEnabled = false;
RuntimeList codeList;
try {
codeList = Universal.can(canArgs, RuntimeContextType.SCALAR);
} finally {
InheritanceResolver.autoloadEnabled = true;
}

if (codeList.size() == 1) {
RuntimeScalar method = codeList.getFirst();
if (method.getBoolean()) {
// Build args: ($package, \&code, @attributes)
RuntimeArray callArgs = new RuntimeArray();
RuntimeArray.push(callArgs, new RuntimeScalar(packageName));
RuntimeArray.push(callArgs, codeRef);
for (String attr : attributes) {
RuntimeArray.push(callArgs, new RuntimeScalar(attr));
}

RuntimeList result = RuntimeCode.apply(method, callArgs, RuntimeContextType.LIST);

// If MODIFY_CODE_ATTRIBUTES returns any values, they are unrecognized attributes
RuntimeArray resultArray = result.getArrayOfAlias();
if (resultArray.size() > 0) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < resultArray.size(); i++) {
if (i > 0) sb.append(", ");
sb.append("\"").append(resultArray.get(i).toString()).append("\"");
}
throw new PerlCompilerException(parser.tokenIndex,
"Invalid CODE attribute" + (resultArray.size() > 1 ? "s" : "") + ": " + sb,
parser.ctx.errorUtil);
}
}
}
}

private static SubroutineNode handleAnonSub(Parser parser, String subName, String prototype, List<String> attributes, BlockNode block, int currentIndex) {
// Now we check if the next token is one of the illegal characters that cannot follow a subroutine.
// These are '(', '{', and '['. If any of these follow, we throw a syntax error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,25 @@ else if (code == null) {
} catch (Exception e) {
// Continue to next @INC entry
}
} else if (hookResultScalar.type == RuntimeScalarType.CODE) {
// Hook returned a CODE reference (line-reader sub)
// Perl calls this repeatedly; the sub sets $_ to each line
// and returns true for more data, false to stop
RuntimeCode lineReader = (RuntimeCode) hookResultScalar.value;
RuntimeScalar dollarUnderscore = GlobalVariable.getGlobalVariable("main::_");
StringBuilder codeBuilder = new StringBuilder();
while (true) {
RuntimeArray readerArgs = new RuntimeArray();
RuntimeBase result = lineReader.apply(readerArgs, RuntimeContextType.SCALAR);
if (result == null || !result.scalar().getBoolean()) {
break;
}
codeBuilder.append(dollarUnderscore.toString()).append("\n");
}
code = codeBuilder.toString();
actualFileName = fileName;
incHookRef = dirScalar;
break;
}
}
// If hook returned undef or we couldn't use the result, continue to next @INC entry
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/perlonjava/runtime/operators/WarnDie.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public static RuntimeScalar catchEval(Throwable e) {
int level = DynamicVariableManager.getLocalLevel();
DynamicVariableManager.pushLocalVariable(sig);

// Temporarily restore eval depth so $^S reads 1 inside the handler.
// By the time we reach catchEval(), evalDepth has already been decremented
// by the eval catch block, but the handler should see $^S=1 since we are
// conceptually still inside eval (Perl 5 calls the handler before unwinding).
RuntimeCode.evalDepth++;
try {
RuntimeCode.apply(sigHandler, args, RuntimeContextType.SCALAR);
} catch (Throwable handlerException) {
Expand All @@ -90,6 +95,7 @@ public static RuntimeScalar catchEval(Throwable e) {
err.set(new RuntimeScalar(ErrorMessageUtil.stringifyException(handlerException)));
}
} finally {
RuntimeCode.evalDepth--;
// Restore $SIG{__DIE__}
DynamicVariableManager.popToLocalLevel(level);
}
Expand Down
Loading
Loading