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
4 changes: 4 additions & 0 deletions src/main/java/org/perlonjava/backend/jvm/EmitOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -1785,6 +1785,10 @@ static void handleCreateReference(EmitterVisitor emitterVisitor, OperatorNode no
// *{EXPR} — EXPR is evaluated in scalar context (e.g. Symbol::qualify_to_ref's
// \*{ qualify $_[0], ... }). LIST context breaks the comma/ternary inside braces.
contextType = RuntimeContextType.SCALAR;
} else if (node.operand instanceof OperatorNode op && op.operator.equals("substr")) {
// \substr(...) must retain the substring proxy so reftype()
// and assignment observe Perl's LVALUE reference.
contextType = RuntimeContextType.SCALAR;
} else if (node.operand instanceof BinaryOperatorNode binOp && binOp.operator.equals("=")) {
contextType = RuntimeContextType.SCALAR;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,13 +254,17 @@ public static RuntimeScalar ref(RuntimeScalar runtimeScalar) {
// Handle nested references
String ref = "REF";
if (runtimeScalar.value instanceof RuntimeScalar scalar) {
ref = switch (scalar.type) {
case VSTRING -> "VSTRING";
case REGEX, ARRAYREFERENCE, HASHREFERENCE, CODE, GLOBREFERENCE, REFERENCE -> "REF";
case GLOB -> "GLOB";
case READONLY_SCALAR -> ref((RuntimeScalar) scalar.value).toString();
default -> "SCALAR";
};
if (scalar instanceof RuntimeSubstrLvalue) {
ref = "LVALUE";
} else {
ref = switch (scalar.type) {
case VSTRING -> "VSTRING";
case REGEX, ARRAYREFERENCE, HASHREFERENCE, CODE, GLOBREFERENCE, REFERENCE -> "REF";
case GLOB -> "GLOB";
case READONLY_SCALAR -> ref((RuntimeScalar) scalar.value).toString();
default -> "SCALAR";
};
}
}
if (runtimeScalar.value == null) {
str = ref;
Expand Down
54 changes: 54 additions & 0 deletions src/main/java/org/perlonjava/runtime/perlmodule/FileChecksum.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.perlonjava.runtime.perlmodule;

import org.perlonjava.runtime.runtimetypes.RuntimeArray;
import org.perlonjava.runtime.runtimetypes.RuntimeList;
import org.perlonjava.runtime.runtimetypes.RuntimeScalar;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

/** Java XS implementation of File::Checksum's portable 16-bit checksum. */
public class FileChecksum extends PerlModuleBase {

public static final String XS_VERSION = "0.01";

public FileChecksum() {
super("File::Checksum", false);
}

public static void initialize() {
FileChecksum module = new FileChecksum();
module.defineExport("EXPORT", "Checksum");
try {
module.registerMethod("Checksum", null);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}

/** Matches Checksum.xs: native-endian 16-bit words, folded and complemented. */
public static RuntimeList Checksum(RuntimeArray args, int ctx) {
if (args.size() < 2) {
return new RuntimeScalar(0).getList();
}

long sum = 0;
int count = args.get(1).getInt();
try {
byte[] bytes = Files.readAllBytes(Path.of(args.get(0).toString()));
int limit = Math.min(Math.max(count, 0), bytes.length);
// PerlOnJava runs on the supported little-endian platforms. The XS
// implementation reads an unsigned short directly from FILE*.
for (int i = 0; i + 1 < limit; i += 2) {
sum += (bytes[i] & 0xffL) | ((bytes[i + 1] & 0xffL) << 8);
}
} catch (IOException | RuntimeException ignored) {
return new RuntimeScalar(0).getList();
}

sum = (sum >>> 16) + (sum & 0xffff);
sum += sum >>> 16;
return new RuntimeScalar((~sum) & 0xffff).getList();
}
}
20 changes: 19 additions & 1 deletion src/main/java/org/perlonjava/runtime/perlmodule/POSIX.java
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,26 @@ public static RuntimeList mktime(RuntimeArray args, int ctx) {
int actualMon = mon + 1;

try {
LocalDateTime ldt = LocalDateTime.of(actualYear, actualMon, mday, hour, min, sec);
// mktime normalizes out-of-range fields (Time::Simple deliberately
// passes hour-1 during DST), so construct from midnight and add the
// supplied hour instead of rejecting hour == -1.
LocalDateTime ldt = LocalDateTime.of(actualYear, actualMon, mday, 0, 0, 0)
.plusHours(hour).plusMinutes(min).plusSeconds(sec);
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
// Perl's mktime honors an explicitly supplied isdst flag. Time::Simple
// passes isdst=0 after subtracting the current DST hour; Java's default
// resolution otherwise leaves the value one hour early during DST.
if (args.size() >= 9) {
int isdst = args.get(8).getInt();
if (isdst == 0) {
ZoneId zone = ZoneId.systemDefault();
int daylightSeconds = zdt.getOffset().getTotalSeconds()
- zone.getRules().getStandardOffset(zdt.toInstant()).getTotalSeconds();
if (daylightSeconds > 0) {
zdt = zdt.plusSeconds(daylightSeconds);
}
}
}
return new RuntimeScalar(zdt.toEpochSecond()).getList();
} catch (Exception e) {
return new RuntimeScalar(-1).getList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,15 @@ public static RuntimeList reftype(RuntimeArray args, int ctx) {
throw new IllegalStateException("Bad number of arguments for reftype() method");
}
RuntimeScalar scalar = magicallyDeref(args.get(0));
if (scalar instanceof RuntimeSubstrLvalue) {
return new RuntimeScalar("LVALUE").getList();
}
String type = switch (scalar.type) {
case REFERENCE -> {
// Inspect the referent to distinguish SCALAR refs from REF (ref-to-ref)
if (scalar.value instanceof RuntimeScalar inner) {
if (inner.type == READONLY_SCALAR) inner = (RuntimeScalar) inner.value;
if (inner instanceof RuntimeSubstrLvalue) yield "LVALUE";
yield switch (inner.type) {
case VSTRING -> "VSTRING";
case REGEX, ARRAYREFERENCE, HASHREFERENCE, CODE, GLOBREFERENCE, REFERENCE -> "REF";
Expand Down
Loading