Skip to content

Commit 3209dbb

Browse files
committed
fix: do not reopen zip file on every resource decode
1 parent d84f038 commit 3209dbb

File tree

6 files changed

+65
-87
lines changed

6 files changed

+65
-87
lines changed

jadx-core/src/main/java/jadx/api/JadxDecompiler.java

+20-21
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ public final class JadxDecompiler implements Closeable {
9999
private final List<ICodeLoader> customCodeLoaders = new ArrayList<>();
100100
private final List<CustomResourcesLoader> customResourcesLoaders = new ArrayList<>();
101101
private final Map<JadxPassType, List<JadxPass>> customPasses = new HashMap<>();
102+
private final List<Closeable> closeableList = new ArrayList<>();
102103

103104
private IJadxEvents events = new JadxEventsImpl();
104105

@@ -158,7 +159,7 @@ private void loadInputFiles() {
158159
loadedInputs.add(loader);
159160
}
160161
} catch (Exception e) {
161-
throw new JadxRuntimeException("Failed to load code for plugin: " + plugin, e);
162+
LOG.warn("Failed to load code for plugin: {}", plugin, e);
162163
}
163164
}
164165
}
@@ -178,32 +179,26 @@ private void reset() {
178179
@Override
179180
public void close() {
180181
reset();
181-
closeInputs();
182-
closeLoaders();
182+
closeAll(loadedInputs);
183+
closeAll(customCodeLoaders);
184+
closeAll(customResourcesLoaders);
185+
closeAll(closeableList);
183186
args.close();
184187
FileUtils.clearTempRootDir();
185188
}
186189

187-
private void closeInputs() {
188-
loadedInputs.forEach(load -> {
189-
try {
190-
load.close();
191-
} catch (Exception e) {
192-
LOG.error("Failed to close input", e);
193-
}
194-
});
195-
loadedInputs.clear();
196-
}
197-
198-
private void closeLoaders() {
199-
for (CustomResourcesLoader resourcesLoader : customResourcesLoaders) {
200-
try {
201-
resourcesLoader.close();
202-
} catch (Exception e) {
203-
LOG.error("Failed to close resource loader: {}", resourcesLoader, e);
190+
private void closeAll(List<? extends Closeable> list) {
191+
try {
192+
for (Closeable closeable : list) {
193+
try {
194+
closeable.close();
195+
} catch (Exception e) {
196+
LOG.warn("Fail to close '{}'", closeable, e);
197+
}
204198
}
199+
} finally {
200+
list.clear();
205201
}
206-
customResourcesLoaders.clear();
207202
}
208203

209204
private void loadPlugins() {
@@ -706,6 +701,10 @@ public ZipReader getZipReader() {
706701
return zipReader;
707702
}
708703

704+
public void addCloseable(Closeable closeable) {
705+
closeableList.add(closeable);
706+
}
707+
709708
@Override
710709
public String toString() {
711710
return "jadx decompiler " + getVersion();

jadx-core/src/main/java/jadx/api/ResourceFile.java

+11-31
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,18 @@
22

33
import java.io.File;
44

5+
import org.jetbrains.annotations.Nullable;
6+
57
import jadx.core.xmlgen.ResContainer;
68
import jadx.core.xmlgen.entry.ResourceEntry;
9+
import jadx.zip.IZipEntry;
710

811
public class ResourceFile {
9-
10-
public static final class ZipRef {
11-
private final File zipFile;
12-
private final String entryName;
13-
14-
public ZipRef(File zipFile, String entryName) {
15-
this.zipFile = zipFile;
16-
this.entryName = entryName;
17-
}
18-
19-
public File getZipFile() {
20-
return zipFile;
21-
}
22-
23-
public String getEntryName() {
24-
return entryName;
25-
}
26-
27-
@Override
28-
public String toString() {
29-
return "ZipRef{" + zipFile + ", '" + entryName + "'}";
30-
}
31-
}
32-
3312
private final JadxDecompiler decompiler;
3413
private final String name;
3514
private final ResourceType type;
36-
private ZipRef zipRef;
15+
16+
private @Nullable IZipEntry zipEntry;
3717
private String deobfName;
3818

3919
public static ResourceFile createResourceFile(JadxDecompiler decompiler, File file, ResourceType type) {
@@ -73,10 +53,6 @@ public ResContainer loadContent() {
7353
return ResourcesLoader.loadContent(decompiler, this);
7454
}
7555

76-
void setZipRef(ZipRef zipRef) {
77-
this.zipRef = zipRef;
78-
}
79-
8056
public boolean setAlias(ResourceEntry ri) {
8157
StringBuilder sb = new StringBuilder();
8258
sb.append("res/").append(ri.getTypeName()).append(ri.getConfig());
@@ -93,8 +69,12 @@ public boolean setAlias(ResourceEntry ri) {
9369
return false;
9470
}
9571

96-
public ZipRef getZipRef() {
97-
return zipRef;
72+
public @Nullable IZipEntry getZipEntry() {
73+
return zipEntry;
74+
}
75+
76+
void setZipEntry(@Nullable IZipEntry zipEntry) {
77+
this.zipEntry = zipEntry;
9878
}
9979

10080
public JadxDecompiler getDecompiler() {

jadx-core/src/main/java/jadx/api/ResourcesLoader.java

+25-28
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import org.slf4j.Logger;
1414
import org.slf4j.LoggerFactory;
1515

16-
import jadx.api.ResourceFile.ZipRef;
1716
import jadx.api.impl.SimpleCodeInfo;
1817
import jadx.api.plugins.CustomResourcesLoader;
1918
import jadx.api.plugins.resources.IResContainerFactory;
@@ -31,7 +30,6 @@
3130
import jadx.core.xmlgen.ResTableBinaryParserProvider;
3231
import jadx.zip.IZipEntry;
3332
import jadx.zip.ZipContent;
34-
import jadx.zip.ZipReader;
3533

3634
import static jadx.core.utils.files.FileUtils.READ_BUFFER_SIZE;
3735
import static jadx.core.utils.files.FileUtils.copyStream;
@@ -40,21 +38,21 @@
4038
public final class ResourcesLoader implements IResourcesLoader {
4139
private static final Logger LOG = LoggerFactory.getLogger(ResourcesLoader.class);
4240

43-
private final JadxDecompiler jadxRef;
41+
private final JadxDecompiler decompiler;
4442

4543
private final List<IResTableParserProvider> resTableParserProviders = new ArrayList<>();
4644
private final List<IResContainerFactory> resContainerFactories = new ArrayList<>();
4745

4846
private BinaryXMLParser binaryXmlParser;
4947

50-
ResourcesLoader(JadxDecompiler jadxRef) {
51-
this.jadxRef = jadxRef;
48+
ResourcesLoader(JadxDecompiler decompiler) {
49+
this.decompiler = decompiler;
5250
this.resTableParserProviders.add(new ResTableBinaryParserProvider());
5351
}
5452

5553
List<ResourceFile> load(RootNode root) {
5654
init(root);
57-
List<File> inputFiles = jadxRef.getArgs().getInputFiles();
55+
List<File> inputFiles = decompiler.getArgs().getInputFiles();
5856
List<ResourceFile> list = new ArrayList<>(inputFiles.size());
5957
for (File file : inputFiles) {
6058
loadFile(list, file);
@@ -95,23 +93,16 @@ public void addResTableParserProvider(IResTableParserProvider resTableParserProv
9593

9694
public static <T> T decodeStream(ResourceFile rf, ResourceDecoder<T> decoder) throws JadxException {
9795
try {
98-
ZipRef zipRef = rf.getZipRef();
99-
if (zipRef == null) {
96+
IZipEntry zipEntry = rf.getZipEntry();
97+
if (zipEntry != null) {
98+
try (InputStream inputStream = zipEntry.getInputStream()) {
99+
return decoder.decode(zipEntry.getUncompressedSize(), inputStream);
100+
}
101+
} else {
100102
File file = new File(rf.getOriginalName());
101103
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
102104
return decoder.decode(file.length(), inputStream);
103105
}
104-
} else {
105-
ZipReader zipReader = rf.getDecompiler().getZipReader();
106-
try (ZipContent content = zipReader.open(zipRef.getZipFile())) {
107-
IZipEntry entry = content.searchEntry(zipRef.getEntryName());
108-
if (entry == null) {
109-
throw new IOException("Zip entry not found: " + zipRef);
110-
}
111-
try (InputStream inputStream = entry.getInputStream()) {
112-
return decoder.decode(entry.getUncompressedSize(), inputStream);
113-
}
114-
}
115106
}
116107
} catch (Exception e) {
117108
throw new JadxException("Error decode: " + rf.getOriginalName(), e);
@@ -194,7 +185,7 @@ private void loadFile(List<ResourceFile> list, File file) {
194185
}
195186

196187
// Try to load the resources with a custom loader first
197-
for (CustomResourcesLoader loader : jadxRef.getCustomResourcesLoaders()) {
188+
for (CustomResourcesLoader loader : decompiler.getCustomResourcesLoaders()) {
198189
if (loader.load(this, list, file)) {
199190
LOG.debug("Custom loader used for {}", file.getAbsolutePath());
200191
return;
@@ -207,13 +198,19 @@ private void loadFile(List<ResourceFile> list, File file) {
207198

208199
public void defaultLoadFile(List<ResourceFile> list, File file, String subDir) {
209200
if (FileUtils.isZipFile(file)) {
210-
jadxRef.getZipReader().visitEntries(file, entry -> {
211-
addEntry(list, file, entry, subDir);
212-
return null;
213-
});
201+
try {
202+
ZipContent zipContent = decompiler.getZipReader().open(file);
203+
// do not close a zip now, entry content will be read later
204+
decompiler.addCloseable(zipContent);
205+
for (IZipEntry entry : zipContent.getEntries()) {
206+
addEntry(list, file, entry, subDir);
207+
}
208+
} catch (Exception e) {
209+
throw new RuntimeException("Failed to open zip file: " + file.getAbsolutePath(), e);
210+
}
214211
} else {
215212
ResourceType type = ResourceType.getFileType(file.getAbsolutePath());
216-
list.add(ResourceFile.createResourceFile(jadxRef, file, type));
213+
list.add(ResourceFile.createResourceFile(decompiler, file, type));
217214
}
218215
}
219216

@@ -223,9 +220,9 @@ public void addEntry(List<ResourceFile> list, File zipFile, IZipEntry entry, Str
223220
}
224221
String name = entry.getName();
225222
ResourceType type = ResourceType.getFileType(name);
226-
ResourceFile rf = ResourceFile.createResourceFile(jadxRef, subDir + name, type);
223+
ResourceFile rf = ResourceFile.createResourceFile(decompiler, subDir + name, type);
227224
if (rf != null) {
228-
rf.setZipRef(new ZipRef(zipFile, name));
225+
rf.setZipEntry(entry);
229226
list.add(rf);
230227
}
231228
}
@@ -238,7 +235,7 @@ public static ICodeInfo loadToCodeWriter(InputStream is) throws IOException {
238235

239236
private synchronized BinaryXMLParser loadBinaryXmlParser() {
240237
if (binaryXmlParser == null) {
241-
binaryXmlParser = new BinaryXMLParser(jadxRef.getRoot());
238+
binaryXmlParser = new BinaryXMLParser(decompiler.getRoot());
242239
}
243240
return binaryXmlParser;
244241
}

jadx-gui/src/main/java/jadx/gui/treemodel/ApkSignature.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import jadx.gui.utils.CertificateManager;
2828
import jadx.gui.utils.NLS;
2929
import jadx.gui.utils.UiUtils;
30+
import jadx.zip.IZipEntry;
3031

3132
public class ApkSignature extends JNode {
3233
private static final long serialVersionUID = -9121321926113143407L;
@@ -45,9 +46,9 @@ public static ApkSignature getApkSignature(JadxWrapper wrapper) {
4546
File apkFile = null;
4647
for (ResourceFile resFile : wrapper.getResources()) {
4748
if (resFile.getType() == ResourceType.MANIFEST) {
48-
ResourceFile.ZipRef zipRef = resFile.getZipRef();
49-
if (zipRef != null) {
50-
apkFile = zipRef.getZipFile();
49+
IZipEntry zipEntry = resFile.getZipEntry();
50+
if (zipEntry != null) {
51+
apkFile = zipEntry.getZipFile();
5152
break;
5253
}
5354
}

jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ private JResource getHierarchyResources(List<ResourceFile> resources) {
5656
String splitPathStr = Pattern.quote(File.separator);
5757
for (ResourceFile rf : resources) {
5858
String rfName;
59-
if (rf.getZipRef() != null) {
59+
if (rf.getZipEntry() != null) {
6060
rfName = rf.getDeobfName();
6161
} else {
6262
rfName = new File(rf.getDeobfName()).getName();

jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/factories/ProtoXmlResContainerFactory.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import jadx.core.dex.nodes.RootNode;
1313
import jadx.core.xmlgen.ResContainer;
1414
import jadx.plugins.input.aab.parsers.ResXmlProtoParser;
15+
import jadx.zip.IZipEntry;
1516

1617
public class ProtoXmlResContainerFactory implements IResContainerFactory {
1718
private ResXmlProtoParser xmlParser;
@@ -27,11 +28,11 @@ public void init(RootNode root) {
2728
if (type != ResourceType.XML && type != ResourceType.MANIFEST) {
2829
return null;
2930
}
30-
ResourceFile.ZipRef ref = resFile.getZipRef();
31-
if (ref == null) {
31+
IZipEntry zipEntry = resFile.getZipEntry();
32+
if (zipEntry == null) {
3233
return null;
3334
}
34-
boolean isFromAab = ref.getZipFile().getPath().contains(".aab");
35+
boolean isFromAab = zipEntry.getZipFile().getPath().contains(".aab");
3536
if (!isFromAab) {
3637
return null;
3738
}

0 commit comments

Comments
 (0)