Skip to content

[feature] Restore switch over string #2288

@skylot

Description

@skylot

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.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions