Exception in thread "pool-9-thread-7" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)
at java.util.HashMap$EntryIterator.next(HashMap.java:1479)
at java.util.HashMap$EntryIterator.next(HashMap.java:1477)
at jadx.core.dex.info.ConstStorage$ValueStorage.removeForCls(ConstStorage.java:61)
at jadx.core.dex.info.ConstStorage.removeForClass(ConstStorage.java:100)
at jadx.core.dex.nodes.ClassNode.deepUnload(ClassNode.java:290)
at jadx.core.dex.nodes.ClassNode.deepUnload(ClassNode.java:295)
at jadx.core.dex.nodes.ClassNode.reloadCode(ClassNode.java:284)
at jadx.api.JavaClass.refresh(JavaClass.java:62)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
If the constant already got duplicates - it will have duplicates even after
class reload, won't it?
But removing this constant from duplicates may break constants replacing
(just imagine a class TestClass with public static final int TEST_CONSTANT = 1;
- after reloading TestClass each "(int) 1" litheral will be replaced to
"TEST_CONSTANT" reference in each reloaded class and trivial increments will
become werid expressions (int y = x + 1; will become
int y = x + TestClass.TEST_CONSTANT;)).
This is a microoptimization, which remove unnecessary values.put() and
values.remove() pair of operations if ValueStorage.put() is called for a
known duplicated value.
This may happen because MethodNode.unloadInsnArr() call from BlockSplitter.visit() - after it instructions[] become null.
So, try to reload method before processing its instructions array and bail if insnArr still null even after reloading method.
There is a duplicate control in the ConstStorage.ValueStorage.add() method,
so each constant should be added only once, and not be added on class refresh.
Fixes "Replace constants" failure after renaming any node.
checkInstructions() may fail with NPE:
ERROR - NullPointerException in pass: BlockSplitter in method: com.google.common.primitives.Ints.IntArrayAsList.<init>(int[], int, int):void, dex: out.dex
java.lang.NullPointerException: null
at jadx.core.dex.nodes.MethodNode.checkInstructions(MethodNode.java:159)
at jadx.core.dex.visitors.blocksmaker.BlockSplitter.visit(BlockSplitter.java:49)
at jadx.core.dex.visitors.DepthTraversal.visit(DepthTraversal.java:31)
at jadx.core.dex.visitors.DepthTraversal.lambda$visit$1(DepthTraversal.java:16)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at jadx.core.dex.visitors.DepthTraversal.visit(DepthTraversal.java:16)
at jadx.core.dex.visitors.DepthTraversal.lambda$visit$0(DepthTraversal.java:15)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at jadx.core.dex.visitors.DepthTraversal.visit(DepthTraversal.java:15)
at jadx.core.ProcessClass.process(ProcessClass.java:41)
at jadx.core.ProcessClass.generateCode(ProcessClass.java:58)
at jadx.core.dex.nodes.ClassNode.decompile(ClassNode.java:292)
at jadx.core.dex.nodes.ClassNode.decompile(ClassNode.java:271)
at jadx.core.dex.nodes.ClassNode.refresh(ClassNode.java:303)
at jadx.api.JavaClass.refresh(JavaClass.java:61)
at jadx.gui.treemodel.JClass.refresh(JClass.java:63)
...
This happens because MethodNode.unloadInsnArr() call from BlockSplitter.visit() - after it instructions[] become null.
So, try to reload method before processing its instructions array.
After renaming some classes needs to be redecompiled to reflect new state.
Move recompilation of non-displayed classes to background thread.
This should improve performance on weak machines.