Skip to content

Commit 3bd581a

Browse files
committed
Kotlin: use the same mtimes as Java
Previously Kotlin's use of IntelliJ's VirtualFile interface meant we got the containing JAR file's mtime, not that of the individual file entry.
1 parent ae1f5bb commit 3bd581a

File tree

1 file changed

+48
-1
lines changed

1 file changed

+48
-1
lines changed

java/kotlin-extractor/src/main/java/com/semmle/extractor/java/OdasaOutput.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44
import java.io.File;
55
import java.io.IOException;
66
import java.util.Arrays;
7+
import java.util.Enumeration;
8+
import java.util.HashMap;
79
import java.util.LinkedHashMap;
810
import java.util.Map;
911
import java.util.Objects;
1012
import java.util.regex.Pattern;
13+
import java.util.zip.ZipEntry;
14+
import java.util.zip.ZipFile;
1115

1216
import com.github.codeql.Logger;
1317
import static com.github.codeql.ClassNamesKt.getIrDeclBinaryName;
@@ -547,6 +551,49 @@ else if (majorVersion==0)
547551
(tcv.majorVersion == majorVersion && tcv.minorVersion == minorVersion &&
548552
tcv.lastModified < lastModified);
549553
}
554+
555+
private static Map<String, Map<String, Long>> jarFileEntryTimeStamps = new HashMap<>();
556+
557+
private static Map<String, Long> getZipFileEntryTimeStamps(String path, Logger log) {
558+
try {
559+
Map<String, Long> result = new HashMap<>();
560+
ZipFile zf = new ZipFile(path);
561+
Enumeration<? extends ZipEntry> entries = zf.entries();
562+
while (entries.hasMoreElements()) {
563+
ZipEntry ze = entries.nextElement();
564+
result.put(ze.getName(), ze.getLastModifiedTime().toMillis());
565+
}
566+
return result;
567+
} catch(IOException e) {
568+
log.warn("Failed to get entry timestamps from " + path, e);
569+
return null;
570+
}
571+
}
572+
573+
private static long getVirtualFileTimeStamp(VirtualFile vf, Logger log) {
574+
if (vf.getFileSystem().getProtocol().equals("jar")) {
575+
String[] parts = vf.getPath().split("!/");
576+
if (parts.length == 2) {
577+
String jarFilePath = parts[0];
578+
String entryPath = parts[1];
579+
if (!jarFileEntryTimeStamps.containsKey(jarFilePath)) {
580+
jarFileEntryTimeStamps.put(jarFilePath, getZipFileEntryTimeStamps(jarFilePath, log));
581+
}
582+
Map<String, Long> entryTimeStamps = jarFileEntryTimeStamps.get(jarFilePath);
583+
if (entryTimeStamps != null) {
584+
Long entryTimeStamp = entryTimeStamps.get(entryPath);
585+
if (entryTimeStamp != null)
586+
return entryTimeStamp;
587+
else
588+
log.warn("Couldn't find timestamp for jar file " + jarFilePath + " entry " + entryPath);
589+
}
590+
}
591+
}
592+
593+
// For all files except for jar files, and a fallback in case of I/O problems reading a jar file:
594+
return vf.getTimeStamp();
595+
}
596+
550597
private static TrapClassVersion fromSymbol(IrDeclaration sym, Logger log) {
551598
VirtualFile vf = sym instanceof IrClass ? getIrClassVirtualFile((IrClass)sym) :
552599
sym.getParent() instanceof IrClass ? getIrClassVirtualFile((IrClass)sym.getParent()) :
@@ -583,7 +630,7 @@ public void visit(int version, int access, java.lang.String name, java.lang.Stri
583630
};
584631
(new ClassReader(vf.contentsToByteArray())).accept(versionGetter, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
585632

586-
return new TrapClassVersion(versionStore[0] & 0xffff, versionStore[0] >> 16, vf.getTimeStamp(), "kotlin");
633+
return new TrapClassVersion(versionStore[0] & 0xffff, versionStore[0] >> 16, getVirtualFileTimeStamp(vf, log), "kotlin");
587634
}
588635
catch(IllegalAccessException e) {
589636
log.warn("Failed to read class file version information", e);

0 commit comments

Comments
 (0)