Skip to content

Commit 0b22523

Browse files
committed
feat: support restore of switch over string (basic case)(#2288)
1 parent a7649dd commit 0b22523

File tree

26 files changed

+739
-101
lines changed

26 files changed

+739
-101
lines changed

README.md

Lines changed: 93 additions & 87 deletions
Large diffs are not rendered by default.

jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ private String appendPluginOptions(int maxNamesLen) {
228228
for (PluginContext context : pluginManager.getAllPluginContexts()) {
229229
JadxPluginOptions options = context.getOptions();
230230
if (options != null) {
231-
if (appendPlugin(context.getPluginInfo(), context.getOptions(), sb, maxNamesLen, k)) {
231+
if (appendPlugin(context.getPluginInfo(), context.getOptions(), sb, maxNamesLen)) {
232232
k++;
233233
}
234234
}
@@ -240,12 +240,12 @@ private String appendPluginOptions(int maxNamesLen) {
240240
return "\nPlugin options (-P<name>=<value>):" + sb;
241241
}
242242

243-
private boolean appendPlugin(JadxPluginInfo pluginInfo, JadxPluginOptions options, StringBuilder out, int maxNamesLen, int k) {
243+
private boolean appendPlugin(JadxPluginInfo pluginInfo, JadxPluginOptions options, StringBuilder out, int maxNamesLen) {
244244
List<OptionDescription> descs = options.getOptionsDescriptions();
245245
if (descs.isEmpty()) {
246246
return false;
247247
}
248-
out.append("\n ").append(k).append(") ");
248+
out.append("\n ");
249249
out.append(pluginInfo.getPluginId()).append(": ").append(pluginInfo.getDescription());
250250
for (OptionDescription desc : descs) {
251251
StringBuilder opt = new StringBuilder();

jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ public class JadxCLIArgs {
109109
@Parameter(names = "--no-finally", description = "don't extract finally block")
110110
protected boolean extractFinally = true;
111111

112+
@Parameter(names = "--no-restore-switch-over-string", description = "don't restore switch over string")
113+
protected boolean restoreSwitchOverString = true;
114+
112115
@Parameter(names = "--no-replace-consts", description = "don't replace constant value with matching constant field")
113116
protected boolean replaceConsts = true;
114117

@@ -360,6 +363,7 @@ public JadxArgs toJadxArgs() {
360363
args.setMoveInnerClasses(moveInnerClasses);
361364
args.setAllowInlineKotlinLambda(allowInlineKotlinLambda);
362365
args.setExtractFinally(extractFinally);
366+
args.setRestoreSwitchOverString(restoreSwitchOverString);
363367
args.setRenameFlags(renameFlags);
364368
args.setFsCaseSensitive(fsCaseSensitive);
365369
args.setCommentsLevel(commentsLevel);
@@ -453,6 +457,10 @@ public boolean isExtractFinally() {
453457
return extractFinally;
454458
}
455459

460+
public boolean isRestoreSwitchOverString() {
461+
return restoreSwitchOverString;
462+
}
463+
456464
public Path getUserRenamesMappingsPath() {
457465
return userRenamesMappingsPath;
458466
}

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ public class JadxArgs implements Closeable {
128128
private boolean respectBytecodeAccModifiers = false;
129129
private boolean exportAsGradleProject = false;
130130

131+
private boolean restoreSwitchOverString = true;
132+
131133
private boolean skipXmlPrettyPrint = false;
132134

133135
private boolean fsCaseSensitive;
@@ -544,6 +546,14 @@ public void setExportAsGradleProject(boolean exportAsGradleProject) {
544546
this.exportAsGradleProject = exportAsGradleProject;
545547
}
546548

549+
public boolean isRestoreSwitchOverString() {
550+
return restoreSwitchOverString;
551+
}
552+
553+
public void setRestoreSwitchOverString(boolean restoreSwitchOverString) {
554+
this.restoreSwitchOverString = restoreSwitchOverString;
555+
}
556+
547557
public boolean isSkipXmlPrettyPrint() {
548558
return skipXmlPrettyPrint;
549559
}
@@ -751,7 +761,7 @@ public String makeCodeArgsHash(@Nullable JadxDecompiler decompiler) {
751761
+ resourceNameSource
752762
+ useKotlinMethodsForVarNames
753763
+ insertDebugLines + extractFinally
754-
+ debugInfo + escapeUnicode + replaceConsts
764+
+ debugInfo + escapeUnicode + replaceConsts + restoreSwitchOverString
755765
+ respectBytecodeAccModifiers + fsCaseSensitive + renameFlags
756766
+ commentsLevel + useDxInput + integerFormat
757767
+ "|" + buildPluginsHash(decompiler);
@@ -796,6 +806,7 @@ public String toString() {
796806
+ ", deobfuscationWhitelist=" + deobfuscationWhitelist
797807
+ ", escapeUnicode=" + escapeUnicode
798808
+ ", replaceConsts=" + replaceConsts
809+
+ ", restoreSwitchOverString=" + restoreSwitchOverString
799810
+ ", respectBytecodeAccModifiers=" + respectBytecodeAccModifiers
800811
+ ", exportAsGradleProject=" + exportAsGradleProject
801812
+ ", skipXmlPrettyPrint=" + skipXmlPrettyPrint

jadx-core/src/main/java/jadx/core/Jadx.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import jadx.core.dex.visitors.regions.LoopRegionVisitor;
6363
import jadx.core.dex.visitors.regions.RegionMakerVisitor;
6464
import jadx.core.dex.visitors.regions.ReturnVisitor;
65+
import jadx.core.dex.visitors.regions.SwitchOverStringVisitor;
6566
import jadx.core.dex.visitors.regions.variables.ProcessVariables;
6667
import jadx.core.dex.visitors.rename.CodeRenameVisitor;
6768
import jadx.core.dex.visitors.rename.RenameVisitor;
@@ -170,6 +171,9 @@ public static List<IDexTreeVisitor> getRegionsModePasses(JadxArgs args) {
170171
// regions IR
171172
passes.add(new RegionMakerVisitor());
172173
passes.add(new IfRegionVisitor());
174+
if (args.isRestoreSwitchOverString()) {
175+
passes.add(new SwitchOverStringVisitor());
176+
}
173177
passes.add(new ReturnVisitor());
174178
passes.add(new CleanRegions());
175179

jadx-core/src/main/java/jadx/core/codegen/RegionGen.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,8 @@ private void addCaseKey(ICodeWriter code, InsnArg arg, Object k) throws CodegenE
278278
useField(code, (FieldInfo) k, null);
279279
} else if (k instanceof Integer) {
280280
code.add(TypeGen.literalToString((Integer) k, arg.getType(), mth, fallback));
281+
} else if (k instanceof String) {
282+
code.add('\"').add((String) k).add('\"');
281283
} else {
282284
throw new JadxRuntimeException("Unexpected key in switch: " + (k != null ? k.getClass() : null));
283285
}

jadx-core/src/main/java/jadx/core/dex/attributes/AType.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import jadx.core.codegen.utils.CodeComment;
66
import jadx.core.dex.attributes.nodes.AnonymousClassAttr;
77
import jadx.core.dex.attributes.nodes.ClassTypeVarsAttr;
8+
import jadx.core.dex.attributes.nodes.CodeFeaturesAttr;
89
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
910
import jadx.core.dex.attributes.nodes.EdgeInsnAttr;
1011
import jadx.core.dex.attributes.nodes.EnumClassAttr;
@@ -74,6 +75,7 @@ public final class AType<T extends IJadxAttribute> implements IJadxAttrType<T> {
7475
public static final AType<MethodOverrideAttr> METHOD_OVERRIDE = new AType<>();
7576
public static final AType<MethodTypeVarsAttr> METHOD_TYPE_VARS = new AType<>();
7677
public static final AType<AttrList<TryCatchBlockAttr>> TRY_BLOCKS_LIST = new AType<>();
78+
public static final AType<CodeFeaturesAttr> METHOD_CODE_FEATURES = new AType<>();
7779

7880
// region
7981
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<>();
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package jadx.core.dex.attributes.nodes;
2+
3+
import java.util.EnumSet;
4+
import java.util.Set;
5+
6+
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
7+
import jadx.core.dex.attributes.AType;
8+
import jadx.core.dex.nodes.MethodNode;
9+
10+
public class CodeFeaturesAttr implements IJadxAttribute {
11+
12+
public enum CodeFeature {
13+
/**
14+
* Code contains switch instruction
15+
*/
16+
SWITCH,
17+
}
18+
19+
public static boolean contains(MethodNode mth, CodeFeature feature) {
20+
CodeFeaturesAttr codeFeaturesAttr = mth.get(AType.METHOD_CODE_FEATURES);
21+
if (codeFeaturesAttr == null) {
22+
return false;
23+
}
24+
return codeFeaturesAttr.getCodeFeatures().contains(feature);
25+
}
26+
27+
public static void add(MethodNode mth, CodeFeature feature) {
28+
CodeFeaturesAttr codeFeaturesAttr = mth.get(AType.METHOD_CODE_FEATURES);
29+
if (codeFeaturesAttr == null) {
30+
codeFeaturesAttr = new CodeFeaturesAttr();
31+
mth.addAttr(codeFeaturesAttr);
32+
}
33+
codeFeaturesAttr.getCodeFeatures().add(feature);
34+
}
35+
36+
private final Set<CodeFeature> codeFeatures = EnumSet.noneOf(CodeFeature.class);
37+
38+
public Set<CodeFeature> getCodeFeatures() {
39+
return codeFeatures;
40+
}
41+
42+
@Override
43+
public AType<CodeFeaturesAttr> getAttrType() {
44+
return AType.METHOD_CODE_FEATURES;
45+
}
46+
47+
@Override
48+
public String toAttrString() {
49+
return "CodeFeatures{" + codeFeatures + '}';
50+
}
51+
}

jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33
import java.util.List;
44
import java.util.Objects;
55

6-
import org.jetbrains.annotations.NotNull;
7-
import org.slf4j.Logger;
8-
import org.slf4j.LoggerFactory;
9-
106
import jadx.api.plugins.input.data.ICodeReader;
117
import jadx.api.plugins.input.data.IMethodProto;
128
import jadx.api.plugins.input.data.IMethodRef;
@@ -17,6 +13,8 @@
1713
import jadx.core.Consts;
1814
import jadx.core.dex.attributes.AFlag;
1915
import jadx.core.dex.attributes.AType;
16+
import jadx.core.dex.attributes.nodes.CodeFeaturesAttr;
17+
import jadx.core.dex.attributes.nodes.CodeFeaturesAttr.CodeFeature;
2018
import jadx.core.dex.attributes.nodes.JadxError;
2119
import jadx.core.dex.info.FieldInfo;
2220
import jadx.core.dex.info.MethodInfo;
@@ -35,8 +33,6 @@
3533
import jadx.core.utils.input.InsnDataUtils;
3634

3735
public class InsnDecoder {
38-
private static final Logger LOG = LoggerFactory.getLogger(InsnDecoder.class);
39-
4036
private final MethodNode method;
4137
private final RootNode root;
4238

@@ -54,7 +50,7 @@ public InsnNode[] process(ICodeReader codeReader) {
5450
rawInsn.decode();
5551
insn = decode(rawInsn);
5652
} catch (Exception e) {
57-
method.addError("Failed to decode insn: " + rawInsn + ", method: " + method, e);
53+
method.addError("Failed to decode insn: " + rawInsn, e);
5854
insn = new InsnNode(InsnType.NOP, 0);
5955
insn.addAttr(AType.JADX_ERROR, new JadxError("decode failed: " + e.getMessage(), e));
6056
}
@@ -64,7 +60,6 @@ public InsnNode[] process(ICodeReader codeReader) {
6460
return instructions;
6561
}
6662

67-
@NotNull
6863
protected InsnNode decode(InsnData insn) throws DecodeException {
6964
switch (insn.getOpcode()) {
7065
case NOP:
@@ -514,14 +509,14 @@ protected InsnNode decode(InsnData insn) throws DecodeException {
514509
}
515510
}
516511

517-
@NotNull
518512
private SwitchInsn makeSwitch(InsnData insn, boolean packed) {
519513
SwitchInsn swInsn = new SwitchInsn(InsnArg.reg(insn, 0, ArgType.UNKNOWN), insn.getTarget(), packed);
520514
ICustomPayload payload = insn.getPayload();
521515
if (payload != null) {
522516
swInsn.attachSwitchData(new SwitchData((ISwitchPayload) payload), insn.getTarget());
523517
}
524518
method.add(AFlag.COMPUTE_POST_DOM);
519+
CodeFeaturesAttr.add(method, CodeFeature.SWITCH);
525520
return swInsn;
526521
}
527522

jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,16 @@ public boolean isSameVar(RegisterArg arg) {
291291
return false;
292292
}
293293

294+
public boolean isSameCodeVar(RegisterArg arg) {
295+
if (arg == null) {
296+
return false;
297+
}
298+
if (isRegister()) {
299+
return ((RegisterArg) this).sameCodeVar(arg);
300+
}
301+
return false;
302+
}
303+
294304
protected final <T extends InsnArg> T copyCommonParams(T copy) {
295305
copy.copyAttributesFrom(this);
296306
copy.setParentInsn(parentInsn);

0 commit comments

Comments
 (0)