Skip to content

[core] Compilation issues due to missing generics types (or missed for-each optimization in LoopRegionVisitor?) #2687

@nitram84

Description

@nitram84

Issue details

I recompiled this game https://fruit-cut-mania.apk.gold/ and I had some compilation issues due to missing generics types e.g.

class com.mobaxe.fruitcut.screens.GameScreen

    private void playAgain() {
        /* this.adIsShowed = false; */ // remove ads
        this.healthScore = 3;
        this.score = 0;
        this.drawChopper = true;
        this.fadeTimeAlpha = 0.0f;
        this.fadeTimeAlphaHealth = 0.0f;
        this.fadeTimeAlphaBG = 0.0f;
        this.fadeTimeAlphaBlood = 0.0f;
        this.docPerScreen = 0;
        if (this.dragPos.size() != 0) {
            this.dragPos.clear();
        }
        this.swipeSound.resume();
        this.throwSound.resume();
        this.meatSliceSound.resume();
        this.chopperSound.resume();
        Iterator i$ = this.stage.getActors().iterator(); // should be 'Iterator<Actor> i$ = this.stage.getActors().iterator();
        while (i$.hasNext()) {
            Actor actor = i$.next();
            actor.setVisible(false);
        }
        gameState = GameState.RUNNING;
        /* if (FruitCut.actionResolver != null) { // remove ads
            FruitCut.actionResolver.showAds(1);
        } */
    }

I tracked the issue down and indeed in smali the generics type for Iterator i$ was missing. To reproduce this error I created a minimal sample:

	package generics;

	import java.util.Iterator;

	public class TestMissingGenericsTypes2<T> implements Iterable<T> {

		@Override
		public Iterator<T> iterator() {
			return null;
		}

		public void test(TestMissingGenericsTypes2<String> l) {
			Iterator<String> i = l.iterator(); // <-- This generics type was removed in smali
			while (i.hasNext()) {
				String s = i.next();
				doSomething(s);
			}
		}

		private void doSomething(String s) {
		}
	}

and I edited the class in smali:

.class public Lgenerics/TestMissingGenericsTypes2;
.super Ljava/lang/Object;
.source "TestMissingGenericsTypes2.java"

# interfaces
.implements Ljava/lang/Iterable;


# annotations
.annotation system Ldalvik/annotation/Signature;
    value = {
        "<T:",
        "Ljava/lang/Object;",
        ">",
        "Ljava/lang/Object;",
        "Ljava/lang/Iterable<",
        "TT;>;"
    }
.end annotation


# direct methods
.method public constructor <init>()V
    .registers 1

    .local p0, "this":Lgenerics/TestMissingGenericsTypes2;, "Lgenerics/TestMissingGenericsTypes2<TT;>;"
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method

.method private doSomething(Ljava/lang/String;)V
    .registers 2
    .param p1, "s"    # Ljava/lang/String;

    .local p0, "this":Lgenerics/TestMissingGenericsTypes2;, "Lgenerics/TestMissingGenericsTypes2<TT;>;"
    return-void
.end method


# virtual methods
.method public iterator()Ljava/util/Iterator;
    .registers 2
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "()",
            "Ljava/util/Iterator<",
            "TT;>;"
        }
    .end annotation

    .local p0, "this":Lgenerics/TestMissingGenericsTypes2;, "Lgenerics/TestMissingGenericsTypes2<TT;>;"
    const/4 v0, 0x0

    return-object v0
.end method

.method public test(Lgenerics/TestMissingGenericsTypes2;)V
    .registers 4
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "(",
            "Lgenerics/TestMissingGenericsTypes2<",
            "Ljava/lang/String;",
            ">;)V"
        }
    .end annotation

    .local p0, "this":Lgenerics/TestMissingGenericsTypes2;, "Lgenerics/TestMissingGenericsTypes2<TT;>;"
    .local p1, "l":Lgenerics/TestMissingGenericsTypes2;, "Lgenerics/TestMissingGenericsTypes2<Ljava/lang/String;>;"
    invoke-virtual {p1}, Lgenerics/TestMissingGenericsTypes2;->iterator()Ljava/util/Iterator;

    move-result-object v0

    .local v0, "i":Ljava/util/Iterator; # comment out generics type:  , "Ljava/util/Iterator<Ljava/lang/String;>;"
    :goto_4
    invoke-interface {v0}, Ljava/util/Iterator;->hasNext()Z

    move-result v1

    if-eqz v1, :cond_14

    invoke-interface {v0}, Ljava/util/Iterator;->next()Ljava/lang/Object;

    move-result-object v1

    check-cast v1, Ljava/lang/String;

    .local v1, "s":Ljava/lang/String;
    invoke-direct {p0, v1}, Lgenerics/TestMissingGenericsTypes2;->doSomething(Ljava/lang/String;)V

    .end local v1    # "s":Ljava/lang/String;
    goto :goto_4

    :cond_14
    return-void
.end method

I have some questions here:

  1. What is the expected decompilation result? Should this Iterator-sample decompile to a for-each loop or should it be decompiled to its original code with restored generics types? With a one line change in LoopRegionVisitor I could ignore missing generics by generating for-each loops. I would prepare a PR for this solution, is this is the expected result. See here: nitram84@86eb7a7

The missing generics type should be restored anyway. I don't have a fix for this:

	public void test(TestMissingGenericsTypes2<String> l) {
		Iterator<String> i = l.iterator(); // <-- Remove this generics type in smali
		if (i.hasNext()) {
			String s = i.next();
			doSomething(s);
		}
	}
  1. Which visitor/pass should validate types and check and restore missing generics types? Is there already a suitable visitor for type validation or should a new one be created? Can you give me a hint here?

  2. Just an idea: This is a libGDX based app and there are a lot of libGDX based app out there. I found the recompilation success rate with jadx is high for libGDX based apps. I think it would be possible to write a jadx-plugin to assist recompiling libGDX based apps. Dependencies and versions are easy to detect. One issue would be the gradle template. Normally libGDX generates its own gradle files. I'm using my own libGDX gradle template based on jadx. Following features would be nice for an extended plugin api:

  • provide gradle templates for export by plugins
  • (or) add contributions to gradle files
  • ability to collect dependencies

Relevant log output or stacktrace

-

Provide sample and class/method full name

see above

Jadx version

latest git, 1.5.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions