Skip to content

Commit d26aafd

Browse files
authored
Merge pull request #60 from fglock/fix-commonsense-test
Fix tied @inc handling in require/use operators
2 parents cb5db6b + 4d67e0b commit d26aafd

File tree

2 files changed

+67
-18
lines changed

2 files changed

+67
-18
lines changed

src/main/java/org/perlonjava/operators/ModuleOperators.java

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import java.nio.file.Files;
1414
import java.nio.file.Path;
1515
import java.nio.file.Paths;
16-
import java.util.List;
1716

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

306305
// Check if the filename is an absolute path or starts with ./ or ../
306+
// and if it exists on the filesystem
307307
Path filePath = Paths.get(fileName);
308-
if (filePath.isAbsolute() || fileName.startsWith("./") || fileName.startsWith("../")) {
308+
boolean tryDirectPath = filePath.isAbsolute() || fileName.startsWith("./") || fileName.startsWith("../");
309+
310+
if (tryDirectPath) {
309311
// For absolute or explicit relative paths, resolve using RuntimeIO.getPath
310312
filePath = RuntimeIO.resolvePath(fileName);
311313
if (Files.exists(filePath)) {
@@ -317,42 +319,77 @@ else if (code == null) {
317319
fullName = filePath;
318320
actualFileName = fullName.toString();
319321
}
320-
} else {
321-
// Otherwise, search in INC directories
322-
List<RuntimeScalar> inc = GlobalVariable.getGlobalArray("main::INC").elements;
322+
}
323+
324+
// If we haven't found the file yet, search in INC directories
325+
// This handles:
326+
// 1. Relative module names (e.g., Foo::Bar)
327+
// 2. Absolute/relative paths that don't exist on filesystem (try @INC hooks only)
328+
if (fullName == null) {
329+
// Search in INC directories
330+
RuntimeArray incArray = GlobalVariable.getGlobalArray("main::INC");
323331

324332
// Make sure the jar files are in @INC - the Perl test files can remove it
325333
boolean seen = false;
326-
for (RuntimeBase dir : inc) {
334+
int incSize = incArray.size();
335+
for (int i = 0; i < incSize; i++) {
336+
RuntimeScalar dir = incArray.get(i);
337+
// Handle tied scalars
338+
if (dir.type == RuntimeScalarType.TIED_SCALAR) {
339+
dir = dir.tiedFetch();
340+
}
327341
if (dir.toString().equals(GlobalContext.JAR_PERLLIB)) {
328342
seen = true;
329343
break;
330344
}
331345
}
332346
if (!seen) {
333-
inc.add(new RuntimeScalar(GlobalContext.JAR_PERLLIB));
347+
incArray.push(new RuntimeScalar(GlobalContext.JAR_PERLLIB));
334348
}
335349

336-
for (RuntimeBase dir : inc) {
337-
RuntimeScalar dirScalar = dir.scalar();
350+
// Iterate using indexed access to properly handle tied arrays
351+
incSize = incArray.size();
352+
for (int i = 0; i < incSize; i++) {
353+
RuntimeScalar dirScalar = incArray.get(i);
354+
355+
// If this is a tied scalar, fetch the actual value
356+
if (dirScalar.type == RuntimeScalarType.TIED_SCALAR) {
357+
dirScalar = dirScalar.tiedFetch();
358+
}
359+
360+
// For absolute/relative paths (starting with /, ./, ../), only try hooks
361+
// Regular directory entries should be skipped for such paths
362+
boolean isHook = dirScalar.type == RuntimeScalarType.CODE ||
363+
dirScalar.type == RuntimeScalarType.REFERENCE ||
364+
dirScalar.type == RuntimeScalarType.ARRAYREFERENCE ||
365+
dirScalar.type == RuntimeScalarType.HASHREFERENCE;
366+
367+
if (tryDirectPath && !isHook) {
368+
// Skip regular directory entries for absolute/relative paths
369+
continue;
370+
}
338371

339372
// Check if this @INC entry is a CODE reference, ARRAY reference, or blessed object
340-
if (dirScalar.type == RuntimeScalarType.CODE ||
341-
dirScalar.type == RuntimeScalarType.REFERENCE ||
342-
dirScalar.type == RuntimeScalarType.ARRAYREFERENCE ||
343-
dirScalar.type == RuntimeScalarType.HASHREFERENCE) {
373+
if (isHook) {
344374

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

350-
// Check if it's a filehandle (GLOB) or array ref with filehandle
380+
// Check if it's a filehandle (GLOB), array ref with filehandle, or scalar ref with code
351381
RuntimeScalar filehandle = null;
352382

353383
if (hookResultScalar.type == RuntimeScalarType.GLOB ||
354384
hookResultScalar.type == RuntimeScalarType.GLOBREFERENCE) {
355385
filehandle = hookResultScalar;
386+
} else if (hookResultScalar.type == RuntimeScalarType.REFERENCE) {
387+
// Hook returned a scalar reference - treat the dereferenced value as code
388+
RuntimeScalar derefValue = hookResultScalar.scalarDeref();
389+
code = derefValue.toString();
390+
actualFileName = fileName;
391+
incHookRef = dirScalar;
392+
break;
356393
} else if (hookResultScalar.type == RuntimeScalarType.ARRAYREFERENCE &&
357394
hookResultScalar.value instanceof RuntimeArray) {
358395
RuntimeArray resultArray = (RuntimeArray) hookResultScalar.value;
@@ -382,7 +419,7 @@ else if (code == null) {
382419
}
383420

384421
// Original string handling for directory paths
385-
String dirName = dir.toString();
422+
String dirName = dirScalar.toString();
386423
if (dirName.equals(GlobalContext.JAR_PERLLIB)) {
387424
// Try to find in jar at "src/main/perl/lib"
388425
String resourcePath = "/lib/" + fileName;

src/main/java/org/perlonjava/runtime/RuntimeArray.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -876,13 +876,23 @@ public void dynamicSaveState() {
876876
// Create a new RuntimeArray to save the current state
877877
RuntimeArray currentState = new RuntimeArray();
878878
// Copy the current elements to the new state
879-
currentState.elements = new ArrayList<>(this.elements);
879+
// For tied arrays, preserve the TieArray object
880+
if (this.type == TIED_ARRAY) {
881+
currentState.elements = this.elements; // Keep the TieArray reference
882+
currentState.type = TIED_ARRAY;
883+
} else {
884+
currentState.elements = new ArrayList<>(this.elements);
885+
}
880886
// Copy the current blessId to the new state
881887
currentState.blessId = this.blessId;
882888
// Push the current state onto the stack
883889
dynamicStateStack.push(currentState);
884-
// Clear the array elements
885-
this.elements.clear();
890+
// Clear the array elements (for tied arrays, this calls CLEAR)
891+
if (this.type == TIED_ARRAY) {
892+
TieArray.tiedClear(this);
893+
} else {
894+
this.elements.clear();
895+
}
886896
// Reset the blessId
887897
this.blessId = 0;
888898
}
@@ -900,6 +910,8 @@ public void dynamicRestoreState() {
900910
RuntimeArray previousState = dynamicStateStack.pop();
901911
// Restore the elements from the saved state
902912
this.elements = previousState.elements;
913+
// Restore the type from the saved state (important for tied arrays)
914+
this.type = previousState.type;
903915
// Restore the blessId from the saved state
904916
this.blessId = previousState.blessId;
905917
}

0 commit comments

Comments
 (0)