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
24 changes: 16 additions & 8 deletions src/main/java/org/perlonjava/perlmodule/Exporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ public static RuntimeList exportToLevel(RuntimeArray args, int ctx) {
String caller = callerList.scalar().toString();

// Retrieve the export lists and tags from the package
RuntimeArray export = GlobalVariable.getGlobalArray(packageScalar + "::EXPORT");
RuntimeArray exportOk = GlobalVariable.getGlobalArray(packageScalar + "::EXPORT_OK");
RuntimeHash exportTags = GlobalVariable.getGlobalHash(packageScalar + "::EXPORT_TAGS");
RuntimeArray export = GlobalVariable.getGlobalArray(packageName + "::EXPORT");
RuntimeArray exportOk = GlobalVariable.getGlobalArray(packageName + "::EXPORT_OK");
RuntimeHash exportTags = GlobalVariable.getGlobalHash(packageName + "::EXPORT_TAGS");

// If no specific symbols are requested, default to exporting all symbols in @EXPORT
if (args.size() == 0) {
Expand Down Expand Up @@ -200,9 +200,9 @@ public static RuntimeList exportToLevel(RuntimeArray args, int ctx) {
for (RuntimeBase symbolObj : tagArray.elements) {
String symbolString = symbolObj.toString();

boolean isExported = export.elements.stream()
boolean isExported = export != null && export.elements.stream()
.anyMatch(e -> e.toString().equals(symbolString));
boolean isExportOk = exportOk.elements.stream()
boolean isExportOk = exportOk != null && exportOk.elements.stream()
.anyMatch(e -> e.toString().equals(symbolString));

if (!isExported && !isExportOk && !symbolString.matches("^[$@%*]")) {
Expand All @@ -213,9 +213,9 @@ public static RuntimeList exportToLevel(RuntimeArray args, int ctx) {
} else {
finalSymbolString = "&" + symbolString;
}
isExported = export.elements.stream()
isExported = export != null && export.elements.stream()
.anyMatch(e -> e.toString().equals(finalSymbolString));
isExportOk = exportOk.elements.stream()
isExportOk = exportOk != null && exportOk.elements.stream()
.anyMatch(e -> e.toString().equals(finalSymbolString));
}

Expand Down Expand Up @@ -277,7 +277,15 @@ private static void importFunction(String packageName, String caller, String fun
RuntimeScalar exportSymbol = GlobalVariable.getGlobalCodeRef(packageName + "::" + functionName);
if (exportSymbol.type == RuntimeScalarType.CODE) {
String fullName = caller + "::" + functionName;
GlobalVariable.getGlobalCodeRef(fullName).set(exportSymbol);
RuntimeScalar importedRef = GlobalVariable.getGlobalCodeRef(fullName);
importedRef.set(exportSymbol);

// If the exported symbol is a forward declaration (undefined), annotate it with the source package
// so that AUTOLOAD can be resolved from the original package
if (exportSymbol.value instanceof RuntimeCode code && !code.defined()) {
code.sourcePackage = packageName;
}

// If this function name is an overridable operator (like 'time'), mark it in isSubs
// so the parser knows to treat it as a subroutine call instead of the builtin
if (ParserTables.OVERRIDABLE_OP.contains(functionName)) {
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/org/perlonjava/runtime/RuntimeCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ protected boolean removeEldestEntry(Map.Entry<Class<?>, MethodHandle> eldest) {
// Method context information for next::method support
public String packageName;
public String subName;
// Source package for imported forward declarations (used for AUTOLOAD resolution)
public String sourcePackage = null;
// Flag to indicate this is a symbolic reference created by \&{string} that should always be "defined"
public boolean isSymbolicReference = false;
// Flag to indicate this is a built-in operator
Expand Down Expand Up @@ -587,7 +589,7 @@ public static RuntimeList apply(RuntimeScalar runtimeScalar, RuntimeArray a, int
// Try to find AUTOLOAD for this subroutine
String subroutineName = code.packageName + "::" + code.subName;
if (code.packageName != null && code.subName != null && !subroutineName.isEmpty()) {
// Check if AUTOLOAD exists
// First check if AUTOLOAD exists in the current package
String autoloadString = code.packageName + "::AUTOLOAD";
RuntimeScalar autoload = GlobalVariable.getGlobalCodeRef(autoloadString);
if (autoload.getDefinedBoolean()) {
Expand All @@ -596,6 +598,19 @@ public static RuntimeList apply(RuntimeScalar runtimeScalar, RuntimeArray a, int
// Call AUTOLOAD
return apply(autoload, a, callContext);
}

// If this is an imported forward declaration, check AUTOLOAD in the source package
if (code.sourcePackage != null && !code.sourcePackage.equals(code.packageName)) {
String sourceAutoloadString = code.sourcePackage + "::AUTOLOAD";
RuntimeScalar sourceAutoload = GlobalVariable.getGlobalCodeRef(sourceAutoloadString);
if (sourceAutoload.getDefinedBoolean()) {
// Set $AUTOLOAD name to the original package function name
String sourceSubroutineName = code.sourcePackage + "::" + code.subName;
getGlobalVariable(sourceAutoloadString).set(sourceSubroutineName);
// Call AUTOLOAD from the source package
return apply(sourceAutoload, a, callContext);
}
}
}
throw new PerlCompilerException("Undefined subroutine &" + subroutineName + " called at ");
}
Expand Down