Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Restore switch over string #2288

Closed
skylot opened this issue Sep 24, 2024 · 3 comments
Closed

[feature] Restore switch over string #2288

skylot opened this issue Sep 24, 2024 · 3 comments
Assignees
Labels
Core Issues in jadx-core module new feature
Milestone

Comments

@skylot
Copy link
Owner

skylot commented Sep 24, 2024

Describe your idea

For simple switch over string java compiler generate lots of code, and it will be nice to restore it close to original.
Simple jadx test case:

package jadx.tests.integration.switches;

import org.junit.jupiter.api.Test;

import jadx.tests.api.IntegrationTest;

import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;

public class TestSwitchOverStrings extends IntegrationTest {

  /**
   * Strings 'frewhyh', 'phgafkp' and 'ucguedt' have same hash code.
   */
  public static class TestCls {

    public int test(String str) {
      switch (str) {
        case "frewhyh":
          return 1;
        case "phgafkp":
          return 2;
        case "test":
          return 3;
        case "other":
          return 4;
        default:
          return 0;
      }
    }

    public void check() {
      assertThat(test("frewhyh")).isEqualTo(1);
      assertThat(test("phgafkp")).isEqualTo(2);
      assertThat(test("test")).isEqualTo(3);
      assertThat(test("other")).isEqualTo(4);
      assertThat(test("unknown")).isEqualTo(0);
      assertThat(test("ucguedt")).isEqualTo(0);
    }
  }

  @Test
  public void test() {
    assertThat(getClassNode(TestCls.class))
        .code()
        .doesNotContain("case -603257287:")
        .containsOne("case \"frewhyh\":");
  }
}

Now jadx generate this code for test method:

    public int test(String str) {
      char c = 65535;
      switch (str.hashCode()) {
        case -603257287:
          if (str.equals("frewhyh")) {
            c = 0;
            break;
          }
          if (str.equals("phgafkp")) {
            c = 1;
            break;
          }
          break;
        case 3556498:
          if (str.equals("test")) {
            c = 2;
            break;
          }
          break;
        case 106069776:
          if (str.equals("other")) {
            c = 3;
            break;
          }
          break;
      }
      switch (c) {
        case 0:
          return 1;
        case 1:
          return 2;
        case 2:
          return 3;
        case 3:
          return 4;
        default:
          return 0;
      }
    }

Here we can see that compiler generate:

  • first switch over string hash code
  • one or several equality checks for actual string in case of hash code collision
  • second switch (optional) to run actual case's code

This issue recovers issue #2278, because it was deleted along with draft PRs and author Github account.

@skylot skylot added new feature Core Issues in jadx-core module labels Sep 24, 2024
@skylot skylot added this to the TBD milestone Sep 24, 2024
@skylot skylot self-assigned this Sep 24, 2024
@DataM0del
Copy link

Describe your idea

For simple switch over string java compiler generate lots of code, and it will be nice to restore it close to original. Simple jadx test case:

package jadx.tests.integration.switches;

import org.junit.jupiter.api.Test;

import jadx.tests.api.IntegrationTest;

import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;

public class TestSwitchOverStrings extends IntegrationTest {

  /**
   * Strings 'frewhyh', 'phgafkp' and 'ucguedt' have same hash code.
   */
  public static class TestCls {

    public int test(String str) {
      switch (str) {
        case "frewhyh":
          return 1;
        case "phgafkp":
          return 2;
        case "test":
          return 3;
        case "other":
          return 4;
        default:
          return 0;
      }
    }

    public void check() {
      assertThat(test("frewhyh")).isEqualTo(1);
      assertThat(test("phgafkp")).isEqualTo(2);
      assertThat(test("test")).isEqualTo(3);
      assertThat(test("other")).isEqualTo(4);
      assertThat(test("unknown")).isEqualTo(0);
      assertThat(test("ucguedt")).isEqualTo(0);
    }
  }

  @Test
  public void test() {
    assertThat(getClassNode(TestCls.class))
        .code()
        .doesNotContain("case -603257287:")
        .containsOne("case \"frewhyh\":");
  }
}

Now jadx generate this code for test method:

    public int test(String str) {
      char c = 65535;
      switch (str.hashCode()) {
        case -603257287:
          if (str.equals("frewhyh")) {
            c = 0;
            break;
          }
          if (str.equals("phgafkp")) {
            c = 1;
            break;
          }
          break;
        case 3556498:
          if (str.equals("test")) {
            c = 2;
            break;
          }
          break;
        case 106069776:
          if (str.equals("other")) {
            c = 3;
            break;
          }
          break;
      }
      switch (c) {
        case 0:
          return 1;
        case 1:
          return 2;
        case 2:
          return 3;
        case 3:
          return 4;
        default:
          return 0;
      }
    }

Here we can see that compiler generate:

* first switch over string hash code

* one or several equality checks for actual string in case of hash code collision

* second switch (optional) to run actual case's code

This issue recovers issue #2278, because it was deleted along with draft PRs and author Github account.

The deleted GitHub account in question:
image

@DataM0del
Copy link

@skylot Extra: it seems like GitHub doesn't delete the issue data for suspended accounts (probably for easy recovery), you can see the comments on the issue here via the API link: https://api.github.com/repos/skylot/jadx/issues/2278/comments
Another thing: You can see data from suspended accounts, you can't get ALL of the data like if it wasn't suspended (e.g. repos), but you will get some data (e.g. events: https://api.github.com/users/Real-Packet/events, my original GitHub account: https://api.github.com/users/RealPacket/events).
Anyways, hopefully GitHub won't suspend me for using this problem for good.

@skylot
Copy link
Owner Author

skylot commented Nov 15, 2024

@DataM0del thanks for letting us know. It was very weird that everything just disappear.
Anyway, I opened this issue just to save a good test case, and now this feature is implemented, so I will close this 🙂

@skylot skylot closed this as completed Nov 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Core Issues in jadx-core module new feature
Projects
None yet
Development

No branches or pull requests

2 participants