■ ■ ■ ■ ■ ■ ■
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java
| skipped 56 lines |
57 | 57 | | import jadx.core.utils.BlockUtils; |
58 | 58 | | import jadx.core.utils.InsnList; |
59 | 59 | | import jadx.core.utils.InsnUtils; |
| 60 | + | import jadx.core.utils.ListUtils; |
60 | 61 | | import jadx.core.utils.Utils; |
61 | 62 | | import jadx.core.utils.exceptions.JadxOverflowException; |
62 | 63 | | |
| skipped 20 lines |
83 | 84 | | this.resolvers = Arrays.asList( |
84 | 85 | | this::initTypeBounds, |
85 | 86 | | this::runTypePropagation, |
| 87 | + | this::tryRestoreTypeVarCasts, |
86 | 88 | | this::tryInsertCasts, |
87 | 89 | | this::tryDeduceTypes, |
88 | 90 | | this::trySplitConstInsns, |
| skipped 431 lines |
520 | 522 | | return false; |
521 | 523 | | } |
522 | 524 | | |
523 | | - | @SuppressWarnings("ForLoopReplaceableByWhile") |
| 525 | + | /** |
| 526 | + | * Fix check casts to type var extend type: |
| 527 | + | * <br> |
| 528 | + | * {@code <T extends Comparable> T var = (Comparable) obj; => T var = (T) obj; } |
| 529 | + | */ |
| 530 | + | private boolean tryRestoreTypeVarCasts(MethodNode mth) { |
| 531 | + | int changed = 0; |
| 532 | + | List<SSAVar> mthSVars = mth.getSVars(); |
| 533 | + | for (SSAVar var : mthSVars) { |
| 534 | + | changed += restoreTypeVarCasts(var); |
| 535 | + | } |
| 536 | + | if (changed == 0) { |
| 537 | + | return false; |
| 538 | + | } |
| 539 | + | if (Consts.DEBUG_TYPE_INFERENCE) { |
| 540 | + | mth.addDebugComment("Restore " + changed + " type vars casts"); |
| 541 | + | } |
| 542 | + | initTypeBounds(mth); |
| 543 | + | return runTypePropagation(mth); |
| 544 | + | } |
| 545 | + | |
| 546 | + | private int restoreTypeVarCasts(SSAVar var) { |
| 547 | + | TypeInfo typeInfo = var.getTypeInfo(); |
| 548 | + | Set<ITypeBound> bounds = typeInfo.getBounds(); |
| 549 | + | if (!ListUtils.anyMatch(bounds, t -> t.getType().isGenericType())) { |
| 550 | + | return 0; |
| 551 | + | } |
| 552 | + | List<ITypeBound> casts = ListUtils.filter(bounds, TypeBoundCheckCastAssign.class::isInstance); |
| 553 | + | if (casts.isEmpty()) { |
| 554 | + | return 0; |
| 555 | + | } |
| 556 | + | ArgType bestType = selectBestTypeFromBounds(bounds).orElse(ArgType.UNKNOWN); |
| 557 | + | if (!bestType.isGenericType()) { |
| 558 | + | return 0; |
| 559 | + | } |
| 560 | + | List<ArgType> extendTypes = bestType.getExtendTypes(); |
| 561 | + | if (extendTypes.size() != 1) { |
| 562 | + | return 0; |
| 563 | + | } |
| 564 | + | int fixed = 0; |
| 565 | + | ArgType extendType = extendTypes.get(0); |
| 566 | + | for (ITypeBound bound : casts) { |
| 567 | + | TypeBoundCheckCastAssign cast = (TypeBoundCheckCastAssign) bound; |
| 568 | + | ArgType castType = cast.getType(); |
| 569 | + | TypeCompareEnum result = typeUpdate.getTypeCompare().compareTypes(extendType, castType); |
| 570 | + | if (result.isEqual() || result == TypeCompareEnum.NARROW_BY_GENERIC) { |
| 571 | + | cast.getInsn().updateIndex(bestType); |
| 572 | + | fixed++; |
| 573 | + | } |
| 574 | + | } |
| 575 | + | return fixed; |
| 576 | + | } |
| 577 | + | |
| 578 | + | @SuppressWarnings({ "ForLoopReplaceableByWhile", "ForLoopReplaceableByForEach" }) |
524 | 579 | | private boolean tryInsertCasts(MethodNode mth) { |
525 | 580 | | int added = 0; |
526 | 581 | | List<SSAVar> mthSVars = mth.getSVars(); |
| skipped 467 lines |