Skip to content

Using a switch on a class field which is an interface implemented by an Enum without explicitly casting to an enum will use the default branch every time after obfuscating #483

@charon25

Description

@charon25

Replicated in Proguard 7.7.0 and JDK Temurin-21.0.7+6.

Simple reproduction case :

package org.example;

public class TestClass {
  public interface TestInterface {
    int getValue();
  }

  public enum TestEnum implements TestInterface {
    A, B, C, D;

    @Override
    public int getValue() {
      return ordinal();
    }
  }

  private TestInterface m_field;

  public void setField(final TestInterface field) {
    m_field = field;
  }

  public int method1() {
    switch (m_field) {
      case TestEnum.A:
        return 1;
      case TestEnum.B:
        return 2;
      case TestEnum.C:
        return 3;
      case TestEnum.D:
        return 4;
      default:
        return 100;
    }
  }

  public int method2() {
    switch ((TestEnum) m_field) {
      case TestEnum.A:
        return 1;
      case TestEnum.B:
        return 2;
      case TestEnum.C:
        return 3;
      case TestEnum.D:
        return 4;
      default:
        return 100;
    }
  }

  public static void main(String[] args) {
    final TestClass test = new TestClass();
    test.setField(TestEnum.B);
    System.out.println("test.method1()=" + test.method1());
    System.out.println("test.method2()=" + test.method2());
  }
}

The complete project with the Maven's pom.xml is available in the given zip archive ProguardSwitchBug.zip. When running the command to obfuscate and run :

mvn clean package && java -cp target/ProguardSwitchBug-1.0-SNAPSHOT-shaded-obfuscated.jar org.example.TestClass

The output is

test.method1()=100
test.method2()=2

When the expected result is the same for both methods.

When looking at the obfuscated code with the IntelliJ IDEA decompiler, the difference is obvious :

method1 :

b var10000 = this.a;
Objects.requireNonNull(var10000);
b var1 = var10000;
byte var2 = 0;
switch (var1.typeSwitch<invokedynamic>(var1, var2)) {
  ...
}

method2 :

switch (((a)this.a).ordinal()) {
  ...
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions