Projects STRLCPY jadx Commits 683c2dfb
🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
    skipped 170 lines
    171 171   ArgType sup = cls.getSuperClass();
    172 172   if (sup != null
    173 173   && !sup.equals(ArgType.OBJECT)
    174  - && !cls.isEnum()) {
     174 + && !cls.contains(AFlag.REMOVE_SUPER_CLASS)) {
    175 175   clsCode.add("extends ");
    176 176   useClass(clsCode, sup);
    177 177   clsCode.add(' ');
    skipped 649 lines
  • ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java
    skipped 20 lines
    21 21   DONT_GENERATE, // process as usual, but don't output to generated code
    22 22   COMMENT_OUT, // process as usual, but comment insn in generated code
    23 23   REMOVE, // can be completely removed
     24 + REMOVE_SUPER_CLASS, // don't add super class
    24 25   
    25 26   HIDDEN, // instruction used inside other instruction but not listed in args
    26 27   
    skipped 71 lines
  • ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfCondition.java
    skipped 155 lines
    156 156   return i;
    157 157   }
    158 158   if (c.getOp() == IfOp.EQ && c.getB().isFalse()) {
    159  - cond = not(new IfCondition(c.invert()));
     159 + cond = new IfCondition(Mode.NOT, Collections.singletonList(new IfCondition(c.invert())));
    160 160   } else {
    161 161   c.normalize();
    162 162   }
    skipped 189 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java
    skipped 61 lines
    62 62   || insn.getResult() == null) {
    63 63   return;
    64 64   }
    65  - 
    66 65   SSAVar sVar = insn.getResult().getSVar();
    67 66   InsnArg constArg;
    68 67   Runnable onSuccess = null;
    69  - 
    70  - InsnType insnType = insn.getType();
    71  - if (insnType == InsnType.CONST || insnType == InsnType.MOVE) {
    72  - constArg = insn.getArg(0);
    73  - if (!constArg.isLiteral()) {
    74  - return;
    75  - }
    76  - long lit = ((LiteralArg) constArg).getLiteral();
    77  - if (lit == 0 && forbidNullInlines(sVar)) {
    78  - // all usages forbids inlining
    79  - return;
     68 + switch (insn.getType()) {
     69 + case CONST:
     70 + case MOVE: {
     71 + constArg = insn.getArg(0);
     72 + if (!constArg.isLiteral()) {
     73 + return;
     74 + }
     75 + long lit = ((LiteralArg) constArg).getLiteral();
     76 + if (lit == 0 && forbidNullInlines(sVar)) {
     77 + // all usages forbids inlining
     78 + return;
     79 + }
     80 + break;
    80 81   }
    81  - } else if (insnType == InsnType.CONST_STR) {
    82  - if (sVar.isUsedInPhi()) {
    83  - return;
     82 + case CONST_STR: {
     83 + String s = ((ConstStringNode) insn).getString();
     84 + FieldNode f = mth.getParentClass().getConstField(s);
     85 + if (f == null) {
     86 + InsnNode copy = insn.copyWithoutResult();
     87 + constArg = InsnArg.wrapArg(copy);
     88 + } else {
     89 + InsnNode constGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
     90 + constArg = InsnArg.wrapArg(constGet);
     91 + constArg.setType(ArgType.STRING);
     92 + onSuccess = () -> f.addUseIn(mth);
     93 + }
     94 + break;
    84 95   }
    85  - String s = ((ConstStringNode) insn).getString();
    86  - FieldNode f = mth.getParentClass().getConstField(s);
    87  - if (f == null) {
    88  - InsnNode copy = insn.copyWithoutResult();
    89  - constArg = InsnArg.wrapArg(copy);
    90  - } else {
    91  - InsnNode constGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
    92  - constArg = InsnArg.wrapArg(constGet);
    93  - constArg.setType(ArgType.STRING);
    94  - onSuccess = () -> f.addUseIn(mth);
     96 + case CONST_CLASS: {
     97 + if (sVar.isUsedInPhi()) {
     98 + return;
     99 + }
     100 + constArg = InsnArg.wrapArg(insn.copyWithoutResult());
     101 + constArg.setType(ArgType.CLASS);
     102 + break;
    95 103   }
    96  - } else if (insnType == InsnType.CONST_CLASS) {
    97  - if (sVar.isUsedInPhi()) {
     104 + default:
    98 105   return;
    99  - }
    100  - constArg = InsnArg.wrapArg(insn.copyWithoutResult());
    101  - constArg.setType(ArgType.CLASS);
    102  - } else {
    103  - return;
    104 106   }
    105 107   
    106 108   // all check passed, run replace
    skipped 170 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java
    skipped 39 lines
    40 40  import jadx.core.dex.nodes.InsnNode;
    41 41  import jadx.core.dex.nodes.MethodNode;
    42 42  import jadx.core.dex.nodes.RootNode;
     43 +import jadx.core.dex.regions.Region;
     44 +import jadx.core.dex.visitors.regions.CheckRegions;
     45 +import jadx.core.dex.visitors.regions.IfRegionVisitor;
    43 46  import jadx.core.dex.visitors.shrink.CodeShrinkVisitor;
    44 47  import jadx.core.utils.BlockInsnPair;
    45 48  import jadx.core.utils.BlockUtils;
    46 49  import jadx.core.utils.InsnRemover;
    47 50  import jadx.core.utils.InsnUtils;
     51 +import jadx.core.utils.ListUtils;
    48 52  import jadx.core.utils.Utils;
    49 53  import jadx.core.utils.exceptions.JadxException;
     54 +import jadx.core.utils.exceptions.JadxRuntimeException;
    50 55   
    51 56  import static jadx.core.utils.InsnUtils.checkInsnType;
    52 57  import static jadx.core.utils.InsnUtils.getSingleArg;
    skipped 2 lines
    55 60  @JadxVisitor(
    56 61   name = "EnumVisitor",
    57 62   desc = "Restore enum classes",
    58  - runAfter = { CodeShrinkVisitor.class, ModVisitor.class, ReSugarCode.class },
    59  - runBefore = { ExtractFieldInit.class }
     63 + runAfter = {
     64 + CodeShrinkVisitor.class, // all possible instructions already inlined
     65 + ModVisitor.class,
     66 + ReSugarCode.class,
     67 + IfRegionVisitor.class, // ternary operator inlined
     68 + CheckRegions.class // regions processing finished
     69 + },
     70 + runBefore = {
     71 + ExtractFieldInit.class
     72 + }
    60 73  )
    61 74  public class EnumVisitor extends AbstractVisitor {
    62 75   
    skipped 29 lines
    92 105   AccessInfo accessFlags = cls.getAccessFlags();
    93 106   if (accessFlags.isEnum()) {
    94 107   cls.setAccessFlags(accessFlags.remove(AccessFlags.ENUM));
    95  - cls.addWarnComment("Failed to restore enum class, 'enum' modifier removed");
     108 + cls.addWarnComment("Failed to restore enum class, 'enum' modifier and super class removed");
    96 109   }
    97 110   }
    98 111   return true;
    skipped 3 lines
    102 115   if (!cls.isEnum()) {
    103 116   return false;
    104 117   }
     118 + ArgType superType = cls.getSuperClass();
     119 + if (superType != null && superType.getObject().equals(ArgType.ENUM.getObject())) {
     120 + cls.add(AFlag.REMOVE_SUPER_CLASS);
     121 + }
    105 122   MethodNode classInitMth = cls.getClassInitMth();
    106 123   if (classInitMth == null) {
    107 124   cls.addWarnComment("Enum class init method not found");
    108 125   return false;
    109 126   }
    110  - if (classInitMth.getBasicBlocks().isEmpty()) {
     127 + Region staticRegion = classInitMth.getRegion();
     128 + if (staticRegion == null || classInitMth.getBasicBlocks().isEmpty()) {
    111 129   return false;
    112 130   }
     131 + if (!ListUtils.allMatch(staticRegion.getSubBlocks(), BlockNode.class::isInstance)) {
     132 + cls.addWarnComment("Unexpected branching instructions in enum static init block");
     133 + return false;
     134 + }
     135 + List<BlockNode> staticBlocks = ListUtils.map(staticRegion.getSubBlocks(), BlockNode.class::cast);
    113 136   ArgType clsType = cls.getClassInfo().getType();
    114 137   
    115 138   // search "$VALUES" field (holds all enum values)
    skipped 21 lines
    137 160   return false;
    138 161   }
    139 162   FieldNode valuesField = valuesCandidates.get(0);
    140  - List<InsnNode> toRemove = new ArrayList<>();
    141 163   
    142 164   // search "$VALUES" array init and collect enum fields
    143 165   BlockInsnPair valuesInitPair = getValuesInitInsn(classInitMth, valuesField);
    skipped 3 lines
    147 169   BlockNode staticBlock = valuesInitPair.getBlock();
    148 170   InsnNode valuesInitInsn = valuesInitPair.getInsn();
    149 171   
     172 + EnumData enumData = new EnumData(cls, valuesField, staticBlocks);
     173 + 
    150 174   List<EnumField> enumFields = null;
    151 175   InsnArg arrArg = valuesInitInsn.getArg(0);
    152 176   if (arrArg.isInsnWrap()) {
    153 177   InsnNode wrappedInsn = ((InsnWrapArg) arrArg).getWrapInsn();
    154  - enumFields = extractEnumFieldsFromInsn(cls, staticBlock, wrappedInsn, toRemove);
     178 + enumFields = extractEnumFieldsFromInsn(enumData, wrappedInsn);
    155 179   }
    156 180   if (enumFields == null) {
     181 + cls.addWarnComment("Unknown enum class pattern. Please report as an issue!");
    157 182   return false;
    158 183   }
    159  - toRemove.add(valuesInitInsn);
     184 + enumData.toRemove.add(valuesInitInsn);
    160 185   
    161 186   // all checks complete, perform transform
    162 187   EnumClassAttr attr = new EnumClassAttr(enumFields);
    skipped 13 lines
    176 201   fieldNode.getFieldInfo().setAlias(name);
    177 202   }
    178 203   fieldNode.add(AFlag.DONT_GENERATE);
    179  - processConstructorInsn(cls, enumField, classInitMth, staticBlock, toRemove);
     204 + processConstructorInsn(enumData, enumField, classInitMth);
    180 205   }
    181 206   valuesField.add(AFlag.DONT_GENERATE);
    182  - InsnRemover.removeAllAndUnbind(classInitMth, staticBlock, toRemove);
     207 + InsnRemover.removeAllAndUnbind(classInitMth, enumData.toRemove);
    183 208   if (classInitMth.countInsns() == 0) {
    184 209   classInitMth.add(AFlag.DONT_GENERATE);
    185  - } else if (!toRemove.isEmpty()) {
     210 + } else if (!enumData.toRemove.isEmpty()) {
    186 211   CodeShrinkVisitor.shrinkMethod(classInitMth);
    187 212   }
    188 213   removeEnumMethods(cls, clsType, valuesField);
    189 214   return true;
    190 215   }
    191 216   
    192  - private void processConstructorInsn(ClassNode cls, EnumField enumField, MethodNode classInitMth,
    193  - BlockNode staticBlock, List<InsnNode> toRemove) {
     217 + private void processConstructorInsn(EnumData data, EnumField enumField, MethodNode classInitMth) {
    194 218   ConstructorInsn co = enumField.getConstrInsn();
    195 219   ClassInfo enumClsInfo = co.getClassType();
    196  - if (!enumClsInfo.equals(cls.getClassInfo())) {
    197  - ClassNode enumCls = cls.root().resolveClass(enumClsInfo);
     220 + if (!enumClsInfo.equals(data.cls.getClassInfo())) {
     221 + ClassNode enumCls = data.cls.root().resolveClass(enumClsInfo);
    198 222   if (enumCls != null) {
    199  - processEnumCls(cls, enumField, enumCls);
     223 + processEnumCls(data.cls, enumField, enumCls);
    200 224   }
    201 225   }
    202  - List<RegisterArg> regs = new ArrayList<>();
    203  - co.getRegisterArgs(regs);
    204  - if (!regs.isEmpty()) {
    205  - cls.addWarnComment("Init of enum " + enumField.getField().getName() + " can be incorrect");
    206  - }
    207  - MethodNode ctrMth = cls.root().resolveMethod(co.getCallMth());
     226 + MethodNode ctrMth = data.cls.root().resolveMethod(co.getCallMth());
    208 227   if (ctrMth != null) {
    209 228   markArgsForSkip(ctrMth);
    210 229   }
    211 230   RegisterArg coResArg = co.getResult();
    212 231   if (coResArg == null || coResArg.getSVar().getUseList().size() <= 2) {
    213  - toRemove.add(co);
     232 + data.toRemove.add(co);
    214 233   } else {
    215 234   // constructor result used in other places -> replace constructor with enum field get (SGET)
    216 235   IndexInsnNode enumGet = new IndexInsnNode(InsnType.SGET, enumField.getField().getFieldInfo(), 0);
    217 236   enumGet.setResult(coResArg.duplicate());
    218  - BlockUtils.replaceInsn(classInitMth, staticBlock, co, enumGet);
     237 + BlockUtils.replaceInsn(classInitMth, co, enumGet);
    219 238   }
    220 239   }
    221 240   
    222 241   @Nullable
    223  - private List<EnumField> extractEnumFieldsFromInsn(ClassNode cls, BlockNode staticBlock,
    224  - InsnNode wrappedInsn, List<InsnNode> toRemove) {
     242 + private List<EnumField> extractEnumFieldsFromInsn(EnumData enumData, InsnNode wrappedInsn) {
    225 243   switch (wrappedInsn.getType()) {
    226 244   case FILLED_NEW_ARRAY:
    227  - return extractEnumFieldsFromFilledArray(cls, wrappedInsn, staticBlock, toRemove);
     245 + return extractEnumFieldsFromFilledArray(enumData, wrappedInsn);
    228 246   
    229 247   case INVOKE:
    230 248   // handle redirection of values array fill (added in java 15)
    231  - return extractEnumFieldsFromInvoke(cls, staticBlock, (InvokeNode) wrappedInsn, toRemove);
     249 + return extractEnumFieldsFromInvoke(enumData, (InvokeNode) wrappedInsn);
    232 250   
    233 251   case NEW_ARRAY:
    234 252   InsnArg arg = wrappedInsn.getArg(0);
    skipped 8 lines
    243 261   }
    244 262   }
    245 263   
    246  - private List<EnumField> extractEnumFieldsFromInvoke(ClassNode cls, BlockNode staticBlock,
    247  - InvokeNode invokeNode, List<InsnNode> toRemove) {
     264 + private List<EnumField> extractEnumFieldsFromInvoke(EnumData enumData, InvokeNode invokeNode) {
    248 265   MethodInfo callMth = invokeNode.getCallMth();
    249  - MethodNode valuesMth = cls.root().resolveMethod(callMth);
     266 + MethodNode valuesMth = enumData.cls.root().resolveMethod(callMth);
    250 267   if (valuesMth == null || valuesMth.isVoidReturn()) {
    251 268   return null;
    252 269   }
    skipped 3 lines
    256 273   if (wrappedInsn == null) {
    257 274   return null;
    258 275   }
    259  - List<EnumField> enumFields = extractEnumFieldsFromInsn(cls, staticBlock, wrappedInsn, toRemove);
     276 + List<EnumField> enumFields = extractEnumFieldsFromInsn(enumData, wrappedInsn);
    260 277   if (enumFields != null) {
    261 278   valuesMth.add(AFlag.DONT_GENERATE);
    262 279   }
    skipped 16 lines
    279 296   return null;
    280 297   }
    281 298   
    282  - private List<EnumField> extractEnumFieldsFromFilledArray(ClassNode cls, InsnNode arrFillInsn, BlockNode staticBlock,
    283  - List<InsnNode> toRemove) {
     299 + private List<EnumField> extractEnumFieldsFromFilledArray(EnumData enumData, InsnNode arrFillInsn) {
    284 300   List<EnumField> enumFields = new ArrayList<>();
    285 301   for (InsnArg arg : arrFillInsn.getArguments()) {
    286 302   EnumField field = null;
    287 303   if (arg.isInsnWrap()) {
    288 304   InsnNode wrappedInsn = ((InsnWrapArg) arg).getWrapInsn();
    289  - field = processEnumFieldByWrappedInsn(cls, wrappedInsn, staticBlock, toRemove);
     305 + field = processEnumFieldByWrappedInsn(enumData, wrappedInsn);
    290 306   } else if (arg.isRegister()) {
    291  - field = processEnumFieldByRegister(cls, (RegisterArg) arg, staticBlock, toRemove);
     307 + field = processEnumFieldByRegister(enumData, (RegisterArg) arg);
    292 308   }
    293 309   if (field == null) {
    294 310   return null;
    295 311   }
    296 312   enumFields.add(field);
    297 313   }
    298  - toRemove.add(arrFillInsn);
     314 + enumData.toRemove.add(arrFillInsn);
    299 315   return enumFields;
    300 316   }
    301 317   
    302  - private EnumField processEnumFieldByWrappedInsn(ClassNode cls, InsnNode wrappedInsn, BlockNode staticBlock, List<InsnNode> toRemove) {
     318 + private EnumField processEnumFieldByWrappedInsn(EnumData data, InsnNode wrappedInsn) {
    303 319   if (wrappedInsn.getType() == InsnType.SGET) {
    304  - return processEnumFieldByField(cls, wrappedInsn, staticBlock, toRemove);
     320 + return processEnumFieldByField(data, wrappedInsn);
    305 321   }
    306 322   ConstructorInsn constructorInsn = castConstructorInsn(wrappedInsn);
    307 323   if (constructorInsn != null) {
    308  - FieldNode enumFieldNode = createFakeField(cls, "EF" + constructorInsn.getOffset());
    309  - cls.addField(enumFieldNode);
    310  - return createEnumFieldByConstructor(cls, enumFieldNode, constructorInsn);
     324 + FieldNode enumFieldNode = createFakeField(data.cls, "EF" + constructorInsn.getOffset());
     325 + data.cls.addField(enumFieldNode);
     326 + return createEnumFieldByConstructor(data.cls, enumFieldNode, constructorInsn);
    311 327   }
    312 328   return null;
    313 329   }
    314 330   
    315 331   @Nullable
    316  - private EnumField processEnumFieldByField(ClassNode cls, InsnNode sgetInsn, BlockNode staticBlock, List<InsnNode> toRemove) {
     332 + private EnumField processEnumFieldByField(EnumData data, InsnNode sgetInsn) {
    317 333   if (sgetInsn.getType() != InsnType.SGET) {
    318 334   return null;
    319 335   }
    320 336   FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) sgetInsn).getIndex();
    321  - FieldNode enumFieldNode = cls.searchField(fieldInfo);
     337 + FieldNode enumFieldNode = data.cls.searchField(fieldInfo);
    322 338   if (enumFieldNode == null) {
    323 339   return null;
    324 340   }
    325  - InsnNode sputInsn = searchFieldPutInsn(cls, staticBlock, enumFieldNode);
     341 + InsnNode sputInsn = searchFieldPutInsn(data, enumFieldNode);
    326 342   if (sputInsn == null) {
    327 343   return null;
    328 344   }
    skipped 4 lines
    333 349   }
    334 350   RegisterArg sgetResult = sgetInsn.getResult();
    335 351   if (sgetResult == null || sgetResult.getSVar().getUseCount() == 1) {
    336  - toRemove.add(sgetInsn);
     352 + data.toRemove.add(sgetInsn);
    337 353   }
    338  - toRemove.add(sputInsn);
    339  - return createEnumFieldByConstructor(cls, enumFieldNode, co);
     354 + data.toRemove.add(sputInsn);
     355 + return createEnumFieldByConstructor(data.cls, enumFieldNode, co);
    340 356   }
    341 357   
    342 358   @Nullable
    343  - private EnumField processEnumFieldByRegister(ClassNode cls, RegisterArg arg, BlockNode staticBlock, List<InsnNode> toRemove) {
     359 + private EnumField processEnumFieldByRegister(EnumData data, RegisterArg arg) {
    344 360   InsnNode assignInsn = arg.getAssignInsn();
    345 361   if (assignInsn != null && assignInsn.getType() == InsnType.SGET) {
    346  - return processEnumFieldByField(cls, assignInsn, staticBlock, toRemove);
     362 + return processEnumFieldByField(data, assignInsn);
    347 363   }
    348 364   
    349 365   SSAVar ssaVar = arg.getSVar();
    skipped 4 lines
    354 370   if (constrInsn == null || constrInsn.getType() != InsnType.CONSTRUCTOR) {
    355 371   return null;
    356 372   }
    357  - FieldNode enumFieldNode = searchEnumField(cls, ssaVar, toRemove);
     373 + FieldNode enumFieldNode = searchEnumField(data, ssaVar);
    358 374   if (enumFieldNode == null) {
    359  - enumFieldNode = createFakeField(cls, "EF" + arg.getRegNum());
    360  - cls.addField(enumFieldNode);
     375 + enumFieldNode = createFakeField(data.cls, "EF" + arg.getRegNum());
     376 + data.cls.addField(enumFieldNode);
    361 377   }
    362  - return createEnumFieldByConstructor(cls, enumFieldNode, (ConstructorInsn) constrInsn);
     378 + return createEnumFieldByConstructor(data.cls, enumFieldNode, (ConstructorInsn) constrInsn);
    363 379   }
    364 380   
    365 381   private FieldNode createFakeField(ClassNode cls, String name) {
    skipped 6 lines
    372 388   }
    373 389   
    374 390   @Nullable
    375  - private FieldNode searchEnumField(ClassNode cls, SSAVar ssaVar, List<InsnNode> toRemove) {
     391 + private FieldNode searchEnumField(EnumData data, SSAVar ssaVar) {
    376 392   InsnNode sputInsn = ssaVar.getUseList().get(0).getParentInsn();
    377 393   if (sputInsn == null || sputInsn.getType() != InsnType.SPUT) {
    378 394   return null;
    379 395   }
    380 396   FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) sputInsn).getIndex();
    381  - FieldNode enumFieldNode = cls.searchField(fieldInfo);
     397 + FieldNode enumFieldNode = data.cls.searchField(fieldInfo);
    382 398   if (enumFieldNode == null) {
    383 399   return null;
    384 400   }
    385  - toRemove.add(sputInsn);
     401 + data.toRemove.add(sputInsn);
    386 402   return enumFieldNode;
    387 403   }
    388 404   
    skipped 20 lines
    409 425   if (ctrMth == null) {
    410 426   return null;
    411 427   }
     428 + List<RegisterArg> regs = new ArrayList<>();
     429 + co.getRegisterArgs(regs);
     430 + if (!regs.isEmpty()) {
     431 + throw new JadxRuntimeException("Init of enum " + enumFieldNode.getName() + " uses external variables");
     432 + }
    412 433   return new EnumField(enumFieldNode, co);
    413 434   }
    414 435   
    415 436   @Nullable
    416  - private InsnNode searchFieldPutInsn(ClassNode cls, BlockNode staticBlock, FieldNode enumFieldNode) {
    417  - for (InsnNode sputInsn : staticBlock.getInstructions()) {
    418  - if (sputInsn != null && sputInsn.getType() == InsnType.SPUT) {
    419  - FieldInfo f = (FieldInfo) ((IndexInsnNode) sputInsn).getIndex();
    420  - FieldNode fieldNode = cls.searchField(f);
    421  - if (Objects.equals(fieldNode, enumFieldNode)) {
    422  - return sputInsn;
     437 + private InsnNode searchFieldPutInsn(EnumData data, FieldNode enumFieldNode) {
     438 + for (BlockNode block : data.staticBlocks) {
     439 + for (InsnNode sputInsn : block.getInstructions()) {
     440 + if (sputInsn != null && sputInsn.getType() == InsnType.SPUT) {
     441 + FieldInfo f = (FieldInfo) ((IndexInsnNode) sputInsn).getIndex();
     442 + FieldNode fieldNode = data.cls.searchField(f);
     443 + if (Objects.equals(fieldNode, enumFieldNode)) {
     444 + return sputInsn;
     445 + }
    423 446   }
    424 447   }
    425 448   }
    skipped 178 lines
    604 627   }
    605 628   }
    606 629   return null;
     630 + }
     631 + 
     632 + private static class EnumData {
     633 + final ClassNode cls;
     634 + final FieldNode valuesField;
     635 + final List<BlockNode> staticBlocks;
     636 + final List<InsnNode> toRemove = new ArrayList<>();
     637 + 
     638 + public EnumData(ClassNode cls, FieldNode valuesField, List<BlockNode> staticBlocks) {
     639 + this.cls = cls;
     640 + this.valuesField = valuesField;
     641 + this.staticBlocks = staticBlocks;
     642 + }
    607 643   }
    608 644  }
    609 645   
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/visitors/regions/DepthRegionTraversal.java
    skipped 18 lines
    19 19   traverseInternal(mth, visitor, mth.getRegion());
    20 20   }
    21 21   
     22 + public static void traverse(MethodNode mth, IContainer container, IRegionVisitor visitor) {
     23 + traverseInternal(mth, visitor, container);
     24 + }
     25 + 
    22 26   public static void traverseIterative(MethodNode mth, IRegionIterativeVisitor visitor) {
    23 27   boolean repeat;
    24 28   int k = 0;
    skipped 65 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java
    skipped 9 lines
    10 10  import jadx.core.dex.instructions.args.InsnArg;
    11 11  import jadx.core.dex.instructions.args.InsnWrapArg;
    12 12  import jadx.core.dex.instructions.args.RegisterArg;
     13 +import jadx.core.dex.instructions.args.SSAVar;
    13 14  import jadx.core.dex.instructions.mods.TernaryInsn;
    14 15  import jadx.core.dex.nodes.BlockNode;
    15 16  import jadx.core.dex.nodes.IContainer;
    skipped 57 lines
    73 74   return false;
    74 75   }
    75 76   if (elseRegion == null) {
    76  - if (mth.isConstructor()) {
    77  - // force ternary conversion to inline all code in 'super' or 'this' calls
    78  - return processOneBranchTernary(mth, ifRegion);
    79  - }
    80  - return false;
     77 + return processOneBranchTernary(mth, ifRegion);
    81 78   }
    82 79   BlockNode tb = getTernaryInsnBlock(thenRegion);
    83 80   BlockNode eb = getTernaryInsnBlock(elseRegion);
    skipped 9 lines
    93 90   InsnNode thenInsn = tb.getInstructions().get(0);
    94 91   InsnNode elseInsn = eb.getInstructions().get(0);
    95 92   
    96  - if (mth.contains(AFlag.USE_LINES_HINTS)
    97  - && thenInsn.getSourceLine() != elseInsn.getSourceLine()) {
    98  - if (thenInsn.getSourceLine() != 0 && elseInsn.getSourceLine() != 0) {
    99  - // sometimes source lines incorrect
    100  - if (!checkLineStats(thenInsn, elseInsn)) {
    101  - return false;
    102  - }
    103  - } else {
    104  - // no debug info
    105  - if (containsTernary(thenInsn) || containsTernary(elseInsn)) {
    106  - // don't make nested ternary by default
    107  - // TODO: add addition checks
    108  - return false;
    109  - }
    110  - }
     93 + if (!verifyLineHints(mth, thenInsn, elseInsn)) {
     94 + return false;
    111 95   }
    112 96   
    113 97   RegisterArg thenResArg = thenInsn.getResult();
    skipped 70 lines
    184 168   return false;
    185 169   }
    186 170   
     171 + private static boolean verifyLineHints(MethodNode mth, InsnNode thenInsn, InsnNode elseInsn) {
     172 + if (mth.contains(AFlag.USE_LINES_HINTS)
     173 + && thenInsn.getSourceLine() != elseInsn.getSourceLine()) {
     174 + if (thenInsn.getSourceLine() != 0 && elseInsn.getSourceLine() != 0) {
     175 + // sometimes source lines incorrect
     176 + return checkLineStats(thenInsn, elseInsn);
     177 + }
     178 + // don't make nested ternary by default
     179 + // TODO: add addition checks
     180 + return !containsTernary(thenInsn) && !containsTernary(elseInsn);
     181 + }
     182 + return true;
     183 + }
     184 + 
    187 185   private static void clearConditionBlocks(List<BlockNode> conditionBlocks, BlockNode header) {
    188 186   for (BlockNode block : conditionBlocks) {
    189 187   if (block != header) {
    skipped 87 lines
    277 275   return false;
    278 276   }
    279 277   
     278 + @SuppressWarnings("StatementWithEmptyBody")
    280 279   private static void replaceWithTernary(MethodNode mth, IfRegion ifRegion, BlockNode block, InsnNode insn) {
    281 280   RegisterArg resArg = insn.getResult();
    282 281   if (resArg.getSVar().getUseList().size() != 1) {
    skipped 12 lines
    295 294   }
    296 295   if (otherArg == null) {
    297 296   return;
     297 + }
     298 + InsnNode elseAssign = otherArg.getAssignInsn();
     299 + if (mth.isConstructor() || (mth.getParentClass().isEnum() && mth.getMethodInfo().isClassInit())) {
     300 + // forcing ternary inline for constructors (will help in moving super call to the top) and enums
     301 + // skip code style checks
     302 + } else {
     303 + if (elseAssign != null && elseAssign.isConstInsn()) {
     304 + if (!verifyLineHints(mth, insn, elseAssign)) {
     305 + return;
     306 + }
     307 + } else {
     308 + if (insn.getResult().sameCodeVar(otherArg)) {
     309 + // don't use same variable in else branch to prevent: l = (l == 0) ? 1 : l
     310 + return;
     311 + }
     312 + }
    298 313   }
    299 314   
    300 315   // all checks passed
    skipped 1 lines
    302 317   if (!ifRegion.getParent().replaceSubBlock(ifRegion, header)) {
    303 318   return;
    304 319   }
     320 + InsnArg elseArg;
     321 + if (elseAssign != null && elseAssign.isConstInsn()) {
     322 + // inline constant
     323 + SSAVar elseVar = elseAssign.getResult().getSVar();
     324 + if (elseVar.getUseCount() == 1 && elseVar.getOnlyOneUseInPhi() == phiInsn) {
     325 + InsnRemover.remove(mth, elseAssign);
     326 + }
     327 + elseArg = InsnArg.wrapInsnIntoArg(elseAssign);
     328 + } else {
     329 + elseArg = otherArg;
     330 + }
    305 331   TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(),
    306  - phiInsn.getResult(), InsnArg.wrapInsnIntoArg(insn), otherArg);
     332 + phiInsn.getResult(), InsnArg.wrapInsnIntoArg(insn), elseArg);
     333 + ternInsn.simplifyCondition();
     334 + 
    307 335   InsnRemover.unbindResult(mth, insn);
    308 336   InsnList.remove(block, insn);
    309  - 
    310 337   InsnRemover.unbindAllArgs(mth, phiInsn);
    311 338   header.getInstructions().clear();
    312 339   ternInsn.rebindArgs();
    skipped 12 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/utils/InsnRemover.java
    skipped 16 lines
    17 17  import jadx.core.dex.instructions.args.RegisterArg;
    18 18  import jadx.core.dex.instructions.args.SSAVar;
    19 19  import jadx.core.dex.nodes.BlockNode;
     20 +import jadx.core.dex.nodes.IContainer;
    20 21  import jadx.core.dex.nodes.InsnNode;
    21 22  import jadx.core.dex.nodes.MethodNode;
    22 23  import jadx.core.utils.exceptions.JadxRuntimeException;
    skipped 203 lines
    226 227   public static void removeAllAndUnbind(MethodNode mth, BlockNode block, List<InsnNode> insns) {
    227 228   unbindInsns(mth, insns);
    228 229   removeAll(block.getInstructions(), insns);
     230 + }
     231 + 
     232 + public static void removeAllAndUnbind(MethodNode mth, IContainer container, List<InsnNode> insns) {
     233 + unbindInsns(mth, insns);
     234 + RegionUtils.visitBlocks(mth, container, b -> removeAll(b.getInstructions(), insns));
     235 + }
     236 + 
     237 + public static void removeAllAndUnbind(MethodNode mth, List<InsnNode> insns) {
     238 + unbindInsns(mth, insns);
     239 + for (BlockNode block : mth.getBasicBlocks()) {
     240 + removeAll(block.getInstructions(), insns);
     241 + }
    229 242   }
    230 243   
    231 244   public static void removeAllWithoutUnbind(BlockNode block, List<InsnNode> insns) {
    skipped 23 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/utils/RegionUtils.java
    skipped 3 lines
    4 4  import java.util.Collections;
    5 5  import java.util.List;
    6 6  import java.util.Set;
     7 +import java.util.function.Consumer;
    7 8   
    8 9  import org.jetbrains.annotations.Nullable;
    9 10   
    skipped 16 lines
    26 27  import jadx.core.dex.trycatch.CatchAttr;
    27 28  import jadx.core.dex.trycatch.ExceptionHandler;
    28 29  import jadx.core.dex.trycatch.TryCatchBlockAttr;
     30 +import jadx.core.dex.visitors.regions.AbstractRegionVisitor;
     31 +import jadx.core.dex.visitors.regions.DepthRegionTraversal;
    29 32  import jadx.core.utils.exceptions.JadxRuntimeException;
    30 33   
    31 34  public class RegionUtils {
    skipped 440 lines
    472 475   return "Null container variable";
    473 476   }
    474 477   return "Unknown container type: " + container.getClass();
     478 + }
     479 + 
     480 + public static void visitBlocks(MethodNode mth, IContainer container, Consumer<IBlock> visitor) {
     481 + DepthRegionTraversal.traverse(mth, container, new AbstractRegionVisitor() {
     482 + @Override
     483 + public void processBlock(MethodNode mth, IBlock block) {
     484 + visitor.accept(block);
     485 + }
     486 + });
    475 487   }
    476 488  }
    477 489   
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/test/java/jadx/tests/integration/conditions/TestSwitchTryBreak.java
    1  -package jadx.tests.integration.conditions;
    2  - 
    3  -import org.junit.jupiter.api.Test;
    4  - 
    5  -import jadx.NotYetImplemented;
    6  -import jadx.tests.api.IntegrationTest;
    7  - 
    8  -public class TestSwitchTryBreak extends IntegrationTest {
    9  - 
    10  - public static class TestCls {
    11  - 
    12  - public void test(int x) {
    13  - switch (x) {
    14  - case 0:
    15  - return;
    16  - case 1:
    17  - String res;
    18  - if ("android".equals(toString())) {
    19  - res = "hello";
    20  - } else {
    21  - try {
    22  - if (String.CASE_INSENSITIVE_ORDER != null) {
    23  - break;
    24  - }
    25  - res = "hi";
    26  - } catch (Exception e) {
    27  - break;
    28  - }
    29  - }
    30  - System.out.println(res);
    31  - }
    32  - System.out.println("returning");
    33  - }
    34  - }
    35  - 
    36  - @Test
    37  - @NotYetImplemented
    38  - public void test() {
    39  - getClassNode(TestCls.class);
    40  - }
    41  -}
    42  - 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/test/java/jadx/tests/integration/conditions/TestSwitchTryBreak2.java
    1  -package jadx.tests.integration.conditions;
    2  - 
    3  -import org.junit.jupiter.api.Test;
    4  - 
    5  -import jadx.NotYetImplemented;
    6  -import jadx.tests.api.IntegrationTest;
    7  - 
    8  -public class TestSwitchTryBreak2 extends IntegrationTest {
    9  - 
    10  - public static class TestCls {
    11  - 
    12  - public void test(int x) {
    13  - switch (x) {
    14  - case 0:
    15  - return;
    16  - case 1:
    17  - String res;
    18  - if ("android".equals(toString())) {
    19  - res = "hello";
    20  - } else {
    21  - try {
    22  - if (x == 5) {
    23  - break;
    24  - }
    25  - res = "hi";
    26  - } catch (Exception e) {
    27  - break;
    28  - }
    29  - }
    30  - System.out.println(res);
    31  - }
    32  - System.out.println("returning");
    33  - }
    34  - }
    35  - 
    36  - @Test
    37  - @NotYetImplemented
    38  - public void test() {
    39  - getClassNode(TestCls.class);
    40  - }
    41  -}
    42  - 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithTernary.java
     1 +package jadx.tests.integration.enums;
     2 + 
     3 +import jadx.tests.api.IntegrationTest;
     4 +import jadx.tests.api.extensions.profiles.TestProfile;
     5 +import jadx.tests.api.extensions.profiles.TestWithProfiles;
     6 + 
     7 +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
     8 + 
     9 +public class TestEnumsWithTernary extends IntegrationTest {
     10 + 
     11 + public enum TestCls {
     12 + FIRST(useNumber() ? "1" : "A"),
     13 + SECOND(useNumber() ? "2" : "B"),
     14 + ANY(useNumber() ? "1" : "2");
     15 + 
     16 + private final String str;
     17 + 
     18 + TestCls(String str) {
     19 + this.str = str;
     20 + }
     21 + 
     22 + public String getStr() {
     23 + return str;
     24 + }
     25 + 
     26 + public static boolean useNumber() {
     27 + return false;
     28 + }
     29 + }
     30 + 
     31 + @TestWithProfiles({ TestProfile.DX_J8, TestProfile.D8_J8 })
     32 + public void test() {
     33 + noDebugInfo();
     34 + assertThat(getClassNode(TestCls.class))
     35 + .code()
     36 + .containsOne("ANY(useNumber() ? \"1\" : \"2\");")
     37 + .doesNotContain("static {");
     38 + }
     39 +}
     40 + 
Please wait...
Page is in error, reload to recover