Projects STRLCPY jadx Commits fcd58ae7
🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
    skipped 378 lines
    379 379   }
    380 380   }
    381 381   ICodeInfo codeInfo = root.getProcessClasses().generateCode(this);
    382  - codeCache.add(clsRawName, codeInfo);
     382 + if (codeInfo != ICodeInfo.EMPTY) {
     383 + codeCache.add(clsRawName, codeInfo);
     384 + }
    383 385   return codeInfo;
    384 386   }
    385 387   
    skipped 467 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java
    skipped 102 lines
    103 103   }
    104 104   }
    105 105   
     106 + public static void deleteFileIfExists(Path filePath) throws IOException {
     107 + Files.deleteIfExists(filePath);
     108 + }
     109 + 
    106 110   public static boolean deleteDir(File dir) {
    107 111   File[] content = dir.listFiles();
    108 112   if (content != null) {
    skipped 113 lines
    222 226   }
    223 227   }
    224 228   
     229 + public static void writeFile(Path file, String data) throws IOException {
     230 + FileUtils.makeDirsForFile(file);
     231 + Files.write(file, data.getBytes(StandardCharsets.UTF_8));
     232 + }
     233 + 
     234 + public static String readFile(Path textFile) throws IOException {
     235 + return new String(Files.readAllBytes(textFile), StandardCharsets.UTF_8);
     236 + }
     237 + 
    225 238   @NotNull
    226 239   public static File prepareFile(File file) {
    227 240   File saveFile = cutFileName(file);
    skipped 29 lines
    257 270   hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    258 271   }
    259 272   return new String(hexChars, StandardCharsets.UTF_8);
     273 + }
     274 + 
     275 + /**
     276 + * Zero padded hex string for first byte
     277 + */
     278 + public static String byteToHex(int value) {
     279 + int v = value & 0xFF;
     280 + byte[] hexChars = new byte[] { HEX_ARRAY[v >>> 4], HEX_ARRAY[v & 0x0F] };
     281 + return new String(hexChars, StandardCharsets.US_ASCII);
     282 + }
     283 + 
     284 + /**
     285 + * Zero padded hex string for int value
     286 + */
     287 + public static String intToHex(int value) {
     288 + byte[] hexChars = new byte[8];
     289 + int v = value;
     290 + for (int i = 7; i >= 0; i--) {
     291 + hexChars[i] = HEX_ARRAY[v & 0x0F];
     292 + v >>>= 4;
     293 + }
     294 + return new String(hexChars, StandardCharsets.US_ASCII);
    260 295   }
    261 296   
    262 297   public static boolean isZipFile(File file) {
    skipped 58 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/DiskCodeCache.java
    1 1  package jadx.gui.utils.codecache.disk;
    2 2   
     3 +import java.io.BufferedInputStream;
     4 +import java.io.BufferedOutputStream;
    3 5  import java.io.ByteArrayOutputStream;
     6 +import java.io.DataInputStream;
    4 7  import java.io.DataOutputStream;
    5 8  import java.io.File;
    6 9  import java.io.IOException;
     10 +import java.io.InputStream;
     11 +import java.io.OutputStream;
    7 12  import java.nio.charset.StandardCharsets;
    8  -import java.nio.file.FileVisitResult;
    9 13  import java.nio.file.Files;
    10 14  import java.nio.file.Path;
    11  -import java.nio.file.PathMatcher;
    12  -import java.nio.file.SimpleFileVisitor;
    13  -import java.nio.file.attribute.BasicFileAttributes;
     15 +import java.nio.file.Paths;
    14 16  import java.nio.file.attribute.FileTime;
    15 17  import java.util.Collections;
    16  -import java.util.HashSet;
    17 18  import java.util.List;
    18 19  import java.util.Map;
    19  -import java.util.Set;
    20 20  import java.util.concurrent.ConcurrentHashMap;
    21 21  import java.util.concurrent.ExecutorService;
    22 22  import java.util.concurrent.Executors;
    skipped 11 lines
    34 34  import jadx.core.utils.exceptions.JadxRuntimeException;
    35 35  import jadx.core.utils.files.FileUtils;
    36 36   
     37 +import static java.nio.file.StandardOpenOption.CREATE;
     38 +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
     39 +import static java.nio.file.StandardOpenOption.WRITE;
     40 + 
    37 41  public class DiskCodeCache implements ICodeCache {
    38 42   private static final Logger LOG = LoggerFactory.getLogger(DiskCodeCache.class);
    39 43   
    40  - private static final int DATA_FORMAT_VERSION = 10;
     44 + private static final int DATA_FORMAT_VERSION = 11;
     45 + 
     46 + private static final byte[] JADX_NAMES_MAP_HEADER = "jadxnm".getBytes(StandardCharsets.US_ASCII);
    41 47   
    42 48   private final Path srcDir;
    43 49   private final Path metaDir;
    44 50   private final Path codeVersionFile;
     51 + private final Path namesMapFile;
    45 52   private final String codeVersion;
    46 53   private final CodeMetadataAdapter codeMetadataAdapter;
    47 54   private final ExecutorService writePool;
    48 55   private final Map<String, ICodeInfo> writeOps = new ConcurrentHashMap<>();
    49  - private final Set<String> cachedKeys = Collections.synchronizedSet(new HashSet<>());
     56 + private final Map<String, Integer> namesMap = new ConcurrentHashMap<>();
    50 57   
    51 58   public DiskCodeCache(RootNode root, Path baseDir) {
    52 59   srcDir = baseDir.resolve("sources");
    53 60   metaDir = baseDir.resolve("metadata");
    54 61   codeVersionFile = baseDir.resolve("code-version");
     62 + namesMapFile = baseDir.resolve("names-map");
    55 63   JadxArgs args = root.getArgs();
    56 64   codeVersion = buildCodeVersion(args);
    57 65   writePool = Executors.newFixedThreadPool(args.getThreadsCount());
    58 66   codeMetadataAdapter = new CodeMetadataAdapter(root);
    59 67   if (checkCodeVersion()) {
    60  - collectCachedItems();
     68 + loadNamesMap();
    61 69   } else {
    62 70   reset();
    63 71   }
    skipped 4 lines
    68 76   if (!Files.exists(codeVersionFile)) {
    69 77   return false;
    70 78   }
    71  - String currentCodeVer = readFileToString(codeVersionFile);
     79 + String currentCodeVer = FileUtils.readFile(codeVersionFile);
    72 80   return currentCodeVer.equals(codeVersion);
    73 81   } catch (Exception e) {
    74 82   LOG.warn("Failed to load code version file", e);
    skipped 7 lines
    82 90   LOG.info("Resetting disk code cache, base dir: {}", srcDir.getParent().toAbsolutePath());
    83 91   FileUtils.deleteDirIfExists(srcDir);
    84 92   FileUtils.deleteDirIfExists(metaDir);
     93 + FileUtils.deleteFileIfExists(namesMapFile);
    85 94   FileUtils.makeDirs(srcDir);
    86 95   FileUtils.makeDirs(metaDir);
    87  - writeFile(codeVersionFile, codeVersion);
    88  - cachedKeys.clear();
     96 + FileUtils.writeFile(codeVersionFile, codeVersion);
    89 97   if (LOG.isDebugEnabled()) {
    90 98   LOG.info("Reset done in: {}ms", System.currentTimeMillis() - start);
    91 99   }
    92 100   } catch (Exception e) {
    93 101   throw new JadxRuntimeException("Failed to reset code cache", e);
    94  - }
    95  - }
    96  - 
    97  - private void collectCachedItems() {
    98  - cachedKeys.clear();
    99  - try {
    100  - long start = System.currentTimeMillis();
    101  - PathMatcher matcher = metaDir.getFileSystem().getPathMatcher("glob:**.jadxmd");
    102  - Files.walkFileTree(metaDir, new SimpleFileVisitor<Path>() {
    103  - @Override
    104  - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
    105  - if (matcher.matches(file)) {
    106  - Path relPath = metaDir.relativize(file);
    107  - String filePath = relPath.toString();
    108  - String clsName = filePath.substring(0, filePath.length() - 7).replace(File.separatorChar, '.');
    109  - cachedKeys.add(clsName);
    110  - }
    111  - return FileVisitResult.CONTINUE;
    112  - }
    113  - });
    114  - LOG.info("Found {} classes metadata in disk cache in {} ms, dir: {}", cachedKeys.size(),
    115  - System.currentTimeMillis() - start, metaDir);
    116  - } catch (Exception e) {
    117  - LOG.error("Failed to collect cached items", e);
     102 + } finally {
     103 + namesMap.clear();
    118 104   }
    119 105   }
    120 106   
    skipped 3 lines
    124 110   @Override
    125 111   public void add(String clsFullName, ICodeInfo codeInfo) {
    126 112   writeOps.put(clsFullName, codeInfo);
    127  - cachedKeys.add(clsFullName);
     113 + int clsId = getClsId(clsFullName);
    128 114   writePool.execute(() -> {
    129 115   try {
    130  - writeFile(getJavaFile(clsFullName), codeInfo.getCodeStr());
    131  - codeMetadataAdapter.write(getMetadataFile(clsFullName), codeInfo.getCodeMetadata());
     116 + FileUtils.writeFile(getJavaFile(clsId), codeInfo.getCodeStr());
     117 + codeMetadataAdapter.write(getMetadataFile(clsId), codeInfo.getCodeMetadata());
    132 118   } catch (Exception e) {
    133 119   LOG.error("Failed to write code cache for " + clsFullName, e);
    134 120   remove(clsFullName);
    skipped 13 lines
    148 134   if (wrtCodeInfo != null) {
    149 135   return wrtCodeInfo.getCodeStr();
    150 136   }
    151  - Path javaFile = getJavaFile(clsFullName);
     137 + int clsId = getClsId(clsFullName);
     138 + Path javaFile = getJavaFile(clsId);
    152 139   if (!Files.exists(javaFile)) {
    153 140   return null;
    154 141   }
    155  - return readFileToString(javaFile);
     142 + return FileUtils.readFile(javaFile);
    156 143   } catch (Exception e) {
    157 144   LOG.error("Failed to read class code for {}", clsFullName, e);
    158 145   return null;
    skipped 10 lines
    169 156   if (wrtCodeInfo != null) {
    170 157   return wrtCodeInfo;
    171 158   }
    172  - Path javaFile = getJavaFile(clsFullName);
     159 + int clsId = getClsId(clsFullName);
     160 + Path javaFile = getJavaFile(clsId);
    173 161   if (!Files.exists(javaFile)) {
    174 162   return ICodeInfo.EMPTY;
    175 163   }
    176  - String code = readFileToString(javaFile);
    177  - return codeMetadataAdapter.readAndBuild(getMetadataFile(clsFullName), code);
     164 + String code = FileUtils.readFile(javaFile);
     165 + return codeMetadataAdapter.readAndBuild(getMetadataFile(clsId), code);
    178 166   } catch (Exception e) {
    179 167   LOG.error("Failed to read code cache for {}", clsFullName, e);
    180 168   return ICodeInfo.EMPTY;
    skipped 2 lines
    183 171   
    184 172   @Override
    185 173   public boolean contains(String clsFullName) {
    186  - return cachedKeys.contains(clsFullName);
     174 + return namesMap.containsKey(clsFullName);
    187 175   }
    188 176   
    189 177   @Override
    190 178   public void remove(String clsFullName) {
    191 179   try {
    192 180   LOG.debug("Removing class info from disk: {}", clsFullName);
    193  - cachedKeys.remove(clsFullName);
    194  - Files.deleteIfExists(getJavaFile(clsFullName));
    195  - Files.deleteIfExists(getMetadataFile(clsFullName));
     181 + Integer clsId = namesMap.remove(clsFullName);
     182 + if (clsId != null) {
     183 + Files.deleteIfExists(getJavaFile(clsId));
     184 + Files.deleteIfExists(getMetadataFile(clsId));
     185 + }
    196 186   } catch (Exception e) {
    197 187   throw new JadxRuntimeException("Failed to remove code cache for " + clsFullName, e);
    198 188   }
    199 189   }
    200 190   
    201  - private static String readFileToString(Path textFile) throws IOException {
    202  - return new String(Files.readAllBytes(textFile), StandardCharsets.UTF_8);
    203  - }
    204  - 
    205  - private void writeFile(Path file, String data) {
    206  - try {
    207  - FileUtils.makeDirsForFile(file);
    208  - Files.write(file, data.getBytes(StandardCharsets.UTF_8));
    209  - } catch (Exception e) {
    210  - LOG.error("Failed to write file: {}", file.toAbsolutePath(), e);
    211  - }
    212  - }
    213  - 
    214 191   private String buildCodeVersion(JadxArgs args) {
    215 192   return DATA_FORMAT_VERSION
    216 193   + ":" + args.makeCodeArgsHash()
    skipped 21 lines
    238 215   }
    239 216   }
    240 217   
    241  - private Path getJavaFile(String clsFullName) {
    242  - return srcDir.resolve(clsFullName.replace('.', File.separatorChar) + ".java");
     218 + private int getClsId(String clsFullName) {
     219 + return namesMap.computeIfAbsent(clsFullName, n -> namesMap.size());
     220 + }
     221 + 
     222 + private void saveNamesMap() {
     223 + LOG.debug("Saving names map for disk cache...");
     224 + try (OutputStream fileOutput = Files.newOutputStream(namesMapFile, WRITE, CREATE, TRUNCATE_EXISTING);
     225 + DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fileOutput))) {
     226 + out.write(JADX_NAMES_MAP_HEADER);
     227 + out.writeInt(namesMap.size());
     228 + for (Map.Entry<String, Integer> entry : namesMap.entrySet()) {
     229 + out.writeUTF(entry.getKey());
     230 + out.writeInt(entry.getValue());
     231 + }
     232 + } catch (Exception e) {
     233 + throw new JadxRuntimeException("Failed to save names map file", e);
     234 + }
    243 235   }
    244 236   
    245  - private Path getMetadataFile(String clsFullName) {
    246  - return metaDir.resolve(clsFullName.replace('.', File.separatorChar) + ".jadxmd");
     237 + private void loadNamesMap() {
     238 + if (!Files.exists(namesMapFile)) {
     239 + reset();
     240 + return;
     241 + }
     242 + namesMap.clear();
     243 + try (InputStream fileInput = Files.newInputStream(namesMapFile);
     244 + DataInputStream in = new DataInputStream(new BufferedInputStream(fileInput))) {
     245 + in.skipBytes(JADX_NAMES_MAP_HEADER.length);
     246 + int count = in.readInt();
     247 + for (int i = 0; i < count; i++) {
     248 + String clsName = in.readUTF();
     249 + int clsId = in.readInt();
     250 + namesMap.put(clsName, clsId);
     251 + }
     252 + LOG.info("Found {} classes in disk cache, dir: {}", count, metaDir.getParent());
     253 + } catch (Exception e) {
     254 + throw new JadxRuntimeException("Failed to load names map file", e);
     255 + }
     256 + }
     257 + 
     258 + private Path getJavaFile(int clsId) {
     259 + return srcDir.resolve(getPathForClsId(clsId, ".java"));
     260 + }
     261 + 
     262 + private Path getMetadataFile(int clsId) {
     263 + return metaDir.resolve(getPathForClsId(clsId, ".jadxmd"));
     264 + }
     265 + 
     266 + private Path getPathForClsId(int clsId, String ext) {
     267 + // all classes divided between 256 top level folders
     268 + String firstByte = FileUtils.byteToHex(clsId);
     269 + return Paths.get(firstByte, FileUtils.intToHex(clsId) + ext);
    247 270   }
    248 271   
    249 272   @SuppressWarnings("ResultOfMethodCallIgnored")
    250 273   @Override
    251 274   public void close() throws IOException {
    252 275   try {
     276 + saveNamesMap();
    253 277   writePool.shutdown();
    254 278   writePool.awaitTermination(2, TimeUnit.MINUTES);
    255 279   } catch (InterruptedException e) {
    skipped 5 lines
Please wait...
Page is in error, reload to recover