Status Update
Comments
sg...@google.com <sg...@google.com>
cl...@google.com <cl...@google.com> #2
Ok, there are multiple issues related to this.
Basically multiple enum optimizations assume the result of toString() on an enum field is the field name. This is invalid in the context of library being minified, and then compiled with another program.
cl...@google.com <cl...@google.com> #3
After discussing about this problem, we concluded that:
Without any specific keep rules, the following main method should always be correct, assuming toString() is not overriden and despite multiple compilation (shrunk lib and shrunk app using shrunk lib):
enum MyEnum {FIRST, SECOND};
class Main {
public static void main (String[] args) {
System.out.print(MyEnum.valueOf(MyEnum.FIRST.toString()));
}
That is not the case at this point, and that should be fixed.
However, the following main method should be correct only if there is a keep rule on the enum fields:
enum MyEnum {FIRST, SECOND};
class Main {
public static void main (String[] args) {
System.out.print(MyEnum.valueOf("FIRST"));
}
Since valueOf uses internally reflection on the field name. That is already not working, and that is the expected behavior.
assert "FIRST".equals(FIRST.toString())
is also not valid code, unless the fields are kept.
Some application use for this the following keep rule to keep the enum fields:
-keepclassmembers enum * {
<fields>;
public static **[] values();
public static ** valueOf(java.lang.String);
}
We can also note that the same problem is present for both the methods name and toString.
gu...@gmail.com <gu...@gmail.com> #4
In this case, the issue is slightly more nuanced. The enum usage is entirely within the library. The enum is not exposed to the application. Additionally, the library was compiled with an older version of r8, which didn't have enum.name
inlining. When the app is now compiled with a newer version of r8, it inlines the usage even in library classes. So, the keep rule in the application code doesn't matter. So, other than using an older version of r8, the only other option left is to tell the library developers to recompile the library with a newer r8. This may not always be possible, depending the on the library author.
cl...@google.com <cl...@google.com> #5
To go back to your example, in the method:
public String findMessage(String key) {
if (key == null) {
return null;
}else if (key.contains(MyEnum.FIRST.name())) {
return MyEnum.FIRST.getMessage();
} else if (key.contains(MyEnum.SECOND.name())) {
return MyEnum.SECOND.getMessage();
} else if (key.contains(MyEnum.THIRD.name())) {
return MyEnum.THIRD.getMessage();
} else {
return "No match found";
}
}
The result of MyEnum.FIRST.name() is not guaranteed to be FIRST,
since the implementation of name() internally uses reflection on
MyEnum to return the field name, and there is no keep rule on
MyEnum fields, so that field name can be changed to anything
by R8. So the library implementor has to insert something like:
-keep enum MyEnum { <fields>; }
or
-keepclassmembers enum * { <fields>; }
for its library code to work.
If that code used to work in a version of R8, it's just
coincidental due to other keep rules or unexploited optimization
potential.
cl...@google.com <cl...@google.com> #6
Sorry Guptan, I got confused. Your code is supposed to be working.
We have a bug in the Enum#toString / Enum#name optimizations. I'm working on a fix.
gu...@gmail.com <gu...@gmail.com> #7
Thank you Clement!
ap...@google.com <ap...@google.com> #8
Branch: master
commit 4e65279f7a4c3d1ca08c53e08bf4697f39ebf6e1
Author: Clément Béra <clementbera@google.com>
Date: Wed Jul 08 06:27:04 2020
Fix enum name/toString issue
Bug: 160351050
Change-Id: If382f8bbb8cb7b98105d4a228e808328c88cb3ef
M src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
A src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
M src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
ap...@google.com <ap...@google.com> #9
Branch: 2.1
commit b0c69954e65e2005002a48179dc56b5e2b4b7011
Author: Clément Béra <clementbera@google.com>
Date: Wed Jul 08 09:47:07 2020
Version 2.1.49
Cherry-pick: Fix enum name/toString issue
CL:
Cherry-pick: Enum unboxing: Disable if missing static methods
CL:
Bug: 160351050
Bug: 160535628
Change-Id: I1debfb14bbfa3b75cea399afc9a711570993aa77
M src/main/java/com/android/tools/r8/Version.java
M src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
M src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
M src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
A src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
A src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java
M src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
ap...@google.com <ap...@google.com> #10
Branch: 2.0
commit 73dbe4acd1233caa4cd224b1fcc82eb605419110
Author: Clément Béra <clementbera@google.com>
Date: Wed Jul 08 09:40:47 2020
Version 2.0.94
Cherry-pick: Fix enum name/toString issue
CL:
Bug: 160351050
Change-Id: Ie433d74002ea67b19d00770fe584f892332449c5
M src/main/java/com/android/tools/r8/Version.java
M src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
M src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
ap...@google.com <ap...@google.com> #11
Branch: 2.1
commit bd7da350b815f1f2a585319638e7fcbb6e7b03f6
Author: Clément Béra <clementbera@google.com>
Date: Wed Jul 08 12:08:50 2020
Version 2.1.50
Reland Version 2.1.49
Cherry-pick: Fix enum name/toString issue
CL:
Cherry-pick: Enum unboxing: Disable if missing static methods
CL:
Bug: 160351050
Bug: 160535628
Change-Id: Iaa50db7b83e823875ef9566ee1b77151a3976432
M src/main/java/com/android/tools/r8/Version.java
M src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
M src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
M src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
A src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java
M src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
cl...@google.com <cl...@google.com> #12
Backports to Studio 4.0 and 4.1:
It will get into Android studio 4.0/4.1/4.2, timeline depends on various release schedules, and some reviewers are on vacation, so I can't really give an estimate.
Description
A full repro and instructions available athttps://github.com/guptadeepanshu/r8Issue
Steps to reproduce:
mylib
(a library with obfuscation enabled and AGP 3.5.3 or older):Expectation:
If the text contains one of the three keywords, it should display a message like
this is the first enum value
.Observation:
No match found
is shown for any text that contains only words in caps, likeFIRST
. If the text containsb
, the message corresponding tofirst
is shown. Similarly,c
showssecond
andd
showsthird
.More details:
The issue started due tohttps://r8-review.googlesource.com/c/r8/+/40221 . The first AGP to include it was 3.6.0. The issue exists in all versions since then.
The MyEnum is not inlined in Api class. However, the enum itself is obfuscated. This result is a strange class where the decompiler shows the enum to be of the below form:
mylib
is compiled with 3.5.3. It is also obfuscated before distribution. Since the above change was not present in 3.5, the enumHowever, at runtime, calling
a.b.name()
returnsFIRST
and notb
. I'm not an expert in the bytecode specifications. So, I don't know exactly how it's happening.When such an enum is used in the compilation of the app, where enum name inlining is enabled, it ends up inlining a call to
name()
with the obfuscated name. So,a.b.name()
becomesb
instead ofFIRST
.So, there are two workarounds that are available for the app right now:
android.enableR8=false
ingradle.properties
)It would be greatly appreciated if the inlining was able to handle this obfuscated enums produced by older versions of R8.