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
67 changes: 52 additions & 15 deletions src/main/java/org/perlonjava/operators/ModuleOperators.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

import static org.perlonjava.perlmodule.Feature.featureManager;
import static org.perlonjava.runtime.ExceptionFormatter.findInnermostCause;
Expand Down Expand Up @@ -304,8 +303,11 @@ else if (runtimeScalar.type == RuntimeScalarType.GLOB || runtimeScalar.type == R
else if (code == null) {

// Check if the filename is an absolute path or starts with ./ or ../
// and if it exists on the filesystem
Path filePath = Paths.get(fileName);
if (filePath.isAbsolute() || fileName.startsWith("./") || fileName.startsWith("../")) {
boolean tryDirectPath = filePath.isAbsolute() || fileName.startsWith("./") || fileName.startsWith("../");

if (tryDirectPath) {
// For absolute or explicit relative paths, resolve using RuntimeIO.getPath
filePath = RuntimeIO.resolvePath(fileName);
if (Files.exists(filePath)) {
Expand All @@ -317,42 +319,77 @@ else if (code == null) {
fullName = filePath;
actualFileName = fullName.toString();
}
} else {
// Otherwise, search in INC directories
List<RuntimeScalar> inc = GlobalVariable.getGlobalArray("main::INC").elements;
}

// If we haven't found the file yet, search in INC directories
// This handles:
// 1. Relative module names (e.g., Foo::Bar)
// 2. Absolute/relative paths that don't exist on filesystem (try @INC hooks only)
if (fullName == null) {
// Search in INC directories
RuntimeArray incArray = GlobalVariable.getGlobalArray("main::INC");

// Make sure the jar files are in @INC - the Perl test files can remove it
boolean seen = false;
for (RuntimeBase dir : inc) {
int incSize = incArray.size();
for (int i = 0; i < incSize; i++) {
RuntimeScalar dir = incArray.get(i);
// Handle tied scalars
if (dir.type == RuntimeScalarType.TIED_SCALAR) {
dir = dir.tiedFetch();
}
if (dir.toString().equals(GlobalContext.JAR_PERLLIB)) {
seen = true;
break;
}
}
if (!seen) {
inc.add(new RuntimeScalar(GlobalContext.JAR_PERLLIB));
incArray.push(new RuntimeScalar(GlobalContext.JAR_PERLLIB));
}

for (RuntimeBase dir : inc) {
RuntimeScalar dirScalar = dir.scalar();
// Iterate using indexed access to properly handle tied arrays
incSize = incArray.size();
for (int i = 0; i < incSize; i++) {
RuntimeScalar dirScalar = incArray.get(i);

// If this is a tied scalar, fetch the actual value
if (dirScalar.type == RuntimeScalarType.TIED_SCALAR) {
dirScalar = dirScalar.tiedFetch();
}

// For absolute/relative paths (starting with /, ./, ../), only try hooks
// Regular directory entries should be skipped for such paths
boolean isHook = dirScalar.type == RuntimeScalarType.CODE ||
dirScalar.type == RuntimeScalarType.REFERENCE ||
dirScalar.type == RuntimeScalarType.ARRAYREFERENCE ||
dirScalar.type == RuntimeScalarType.HASHREFERENCE;

if (tryDirectPath && !isHook) {
// Skip regular directory entries for absolute/relative paths
continue;
}

// Check if this @INC entry is a CODE reference, ARRAY reference, or blessed object
if (dirScalar.type == RuntimeScalarType.CODE ||
dirScalar.type == RuntimeScalarType.REFERENCE ||
dirScalar.type == RuntimeScalarType.ARRAYREFERENCE ||
dirScalar.type == RuntimeScalarType.HASHREFERENCE) {
if (isHook) {

RuntimeBase hookResult = tryIncHook(dirScalar, fileName);
if (hookResult != null) {
// Hook returned something useful
RuntimeScalar hookResultScalar = hookResult.scalar();

// Check if it's a filehandle (GLOB) or array ref with filehandle
// Check if it's a filehandle (GLOB), array ref with filehandle, or scalar ref with code
RuntimeScalar filehandle = null;

if (hookResultScalar.type == RuntimeScalarType.GLOB ||
hookResultScalar.type == RuntimeScalarType.GLOBREFERENCE) {
filehandle = hookResultScalar;
} else if (hookResultScalar.type == RuntimeScalarType.REFERENCE) {
// Hook returned a scalar reference - treat the dereferenced value as code
RuntimeScalar derefValue = hookResultScalar.scalarDeref();
code = derefValue.toString();
actualFileName = fileName;
incHookRef = dirScalar;
break;
} else if (hookResultScalar.type == RuntimeScalarType.ARRAYREFERENCE &&
hookResultScalar.value instanceof RuntimeArray) {
RuntimeArray resultArray = (RuntimeArray) hookResultScalar.value;
Expand Down Expand Up @@ -382,7 +419,7 @@ else if (code == null) {
}

// Original string handling for directory paths
String dirName = dir.toString();
String dirName = dirScalar.toString();
if (dirName.equals(GlobalContext.JAR_PERLLIB)) {
// Try to find in jar at "src/main/perl/lib"
String resourcePath = "/lib/" + fileName;
Expand Down
18 changes: 15 additions & 3 deletions src/main/java/org/perlonjava/runtime/RuntimeArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -876,13 +876,23 @@ public void dynamicSaveState() {
// Create a new RuntimeArray to save the current state
RuntimeArray currentState = new RuntimeArray();
// Copy the current elements to the new state
currentState.elements = new ArrayList<>(this.elements);
// For tied arrays, preserve the TieArray object
if (this.type == TIED_ARRAY) {
currentState.elements = this.elements; // Keep the TieArray reference
currentState.type = TIED_ARRAY;
} else {
currentState.elements = new ArrayList<>(this.elements);
}
// Copy the current blessId to the new state
currentState.blessId = this.blessId;
// Push the current state onto the stack
dynamicStateStack.push(currentState);
// Clear the array elements
this.elements.clear();
// Clear the array elements (for tied arrays, this calls CLEAR)
if (this.type == TIED_ARRAY) {
TieArray.tiedClear(this);
} else {
this.elements.clear();
}
// Reset the blessId
this.blessId = 0;
}
Expand All @@ -900,6 +910,8 @@ public void dynamicRestoreState() {
RuntimeArray previousState = dynamicStateStack.pop();
// Restore the elements from the saved state
this.elements = previousState.elements;
// Restore the type from the saved state (important for tied arrays)
this.type = previousState.type;
// Restore the blessId from the saved state
this.blessId = previousState.blessId;
}
Expand Down