|
4 | 4 | import java.io.File;
|
5 | 5 | import java.io.IOException;
|
6 | 6 | import java.util.Arrays;
|
| 7 | +import java.util.Enumeration; |
| 8 | +import java.util.HashMap; |
7 | 9 | import java.util.LinkedHashMap;
|
8 | 10 | import java.util.Map;
|
9 | 11 | import java.util.Objects;
|
10 | 12 | import java.util.regex.Pattern;
|
| 13 | +import java.util.zip.ZipEntry; |
| 14 | +import java.util.zip.ZipFile; |
11 | 15 |
|
12 | 16 | import com.github.codeql.Logger;
|
13 | 17 | import static com.github.codeql.ClassNamesKt.getIrDeclBinaryName;
|
@@ -547,6 +551,51 @@ else if (majorVersion==0)
|
547 | 551 | (tcv.majorVersion == majorVersion && tcv.minorVersion == minorVersion &&
|
548 | 552 | tcv.lastModified < lastModified);
|
549 | 553 | }
|
| 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 | + } else { |
| 591 | + log.warn("Expected JAR-file path " + vf.getPath() + " to have exactly one '!/' separator"); |
| 592 | + } |
| 593 | + } |
| 594 | + |
| 595 | + // For all files except for jar files, and a fallback in case of I/O problems reading a jar file: |
| 596 | + return vf.getTimeStamp(); |
| 597 | + } |
| 598 | + |
550 | 599 | private static TrapClassVersion fromSymbol(IrDeclaration sym, Logger log) {
|
551 | 600 | VirtualFile vf = sym instanceof IrClass ? getIrClassVirtualFile((IrClass)sym) :
|
552 | 601 | sym.getParent() instanceof IrClass ? getIrClassVirtualFile((IrClass)sym.getParent()) :
|
@@ -583,7 +632,7 @@ public void visit(int version, int access, java.lang.String name, java.lang.Stri
|
583 | 632 | };
|
584 | 633 | (new ClassReader(vf.contentsToByteArray())).accept(versionGetter, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
585 | 634 |
|
586 |
| - return new TrapClassVersion(versionStore[0] & 0xffff, versionStore[0] >> 16, vf.getTimeStamp(), "kotlin"); |
| 635 | + return new TrapClassVersion(versionStore[0] & 0xffff, versionStore[0] >> 16, getVirtualFileTimeStamp(vf, log), "kotlin"); |
587 | 636 | }
|
588 | 637 | catch(IllegalAccessException e) {
|
589 | 638 | log.warn("Failed to read class file version information", e);
|
|
0 commit comments