Status Update
Comments
al...@google.com <al...@google.com> #2
I can reproduce this (thanks for the repro project!)
It looks like the problem is that the desugared api list from r8 contains this entry:
java/util/Collection#removeIf(Ljava/util/function/Predicate;)Z
but the bytecode here doesn't match -- it's java/util/ArrayList. Collection isn't a directly implemented interface or a direct super class, it's an interface on the super super class. The most efficient thing runtime wise would be for the signature list to inline this method on all implemented subclasses. But I should probably at least for now go and make the desugared API lookup do something similar to what it does for API lookup -- search through all super classes and interfaces as well. This isn't a new problem, so I'm very surprised this hasn't come up before (or it has, and I've forgotten).
tn...@google.com <tn...@google.com> #5
This is partially fixed now; it's fully fixed for the sample project, but in the scenario I described in comment 4, it works if you also configure library desugaring to be on in the library.
al...@google.com <al...@google.com> #6
likely related:
Objects.requireNonNullElse
Objects.requireNonNullElseGet
are now also showing this same false positive warning.
Android Studio Koala | 2023.3.2 Canary 2 gradle 8.6 plugin: 8.3.1
tn...@google.com <tn...@google.com> #7
This seems unrelated. Can you file a new bug?
Specifically, for Objects.requireNonNullElse
I am not seeing a lint API error, but I am seeing a warning from IntelliJ's built-in Java inspections. These should probably not show up in Android modules. I swear we've fixed that before but I'm assuming something changed to break that.
For Objects.requireNonNullElseGet
I do see a lint API warning for that. But, that's not a recent change, right? I looked at R8's desugaring list and that method is not there. So that method does crash at runtime, right?
al...@google.com <al...@google.com> #8
Can you file a new bug?
fair enough - will do.
I swear we've fixed that before but I'm assuming something changed to break that.
I'm not 100% sure whether it first occurred when I moved to 8.3.1, and/or to Koala Canary 2
So that method does crash at runtime, right?
funnily enough no, even when I run on Android 8.0 (in the emulator); In fact I seem to have been using that method for 2 years now and the first of those 2 years I ran it personally on a physical device (Samsung A5 2017 with Android 8.0 / other users on?) ... and yes, I am flabbergasted as well :)
I'll try and dig a little regarding the requireNonNullElseGet first as indeed I realise it should crash, and then will file a new bug (as requireNonNullElse should work regardless)
I'll add a reference here for your enjoyment :)
au...@google.com <au...@google.com> #9
Actually it looks like D8 is really backporting it; I wrote this test class:
public class JavaTest {
public void test(String s) {
Objects.requireNonNullElse(s, "test");
Objects.requireNonNullElseGet(s, new Supplier<String>() {
@Override
public String get() {
return null;
}
});
}
}
Built the APK and opened the APK analyzer, drilled to this method and invoked Show Bytecode and here's what I get:
# virtual methods
.method public test(Ljava/lang/String;)V
.registers 3
.param p1, "s" # Ljava/lang/String;
.line 8
const-string v0, "test"
invoke-static {p1, v0}, Lcom/example/myapplication/JavaTest$$ExternalSyntheticBackport0;->m(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
.line 9
new-instance v0, Lcom/example/myapplication/JavaTest$1;
invoke-direct {v0, p0}, Lcom/example/myapplication/JavaTest$1;-><init>(Lcom/example/myapplication/JavaTest;)V
invoke-static {p1, v0}, Lcom/example/myapplication/JavaTest$$ExternalSyntheticBackport1;->m(Ljava/lang/Object;Ljava/util/function/Supplier;)Ljava/lang/Object;
.line 15
return-void
.end method
So both methods are desugared.
Clement/Søren, I don't see this in the backport list:
$ java -cp $ANDROID_HOME/cmdline-tools/latest/lib/r8.jar com.android.tools.r8.BackportedMethodList --min-api 15 | grep requireNonNullElse
java/util/Objects#requireNonNullElse(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
(requireNonNullElse
is there, requireNonNullElseGet
is not.)
al...@google.com <al...@google.com> #10
aha - so requireNonNullElseGet is not magical after all :)
Please let me know if I should still file a new bug.
tx
pa...@google.com <pa...@google.com> #11
The method Objects.requireNonNullElseGet
is a bit special in terms of backporting, as it takes an argument of type Supplier
, which was added in API level 24. For pure backporting (no desugared library) this method can only be backported from API level 24:
$ java -cp $ANDROID_HOME/cmdline-tools/latest/lib/r8.jar com.android.tools.r8.BackportedMethodList --min-api 24 | grep requireNonNullElse
java/util/Objects#requireNonNullElse(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
java/util/Objects#requireNonNullElseGet(Ljava/lang/Object;Ljava/util/function/Supplier;)Ljava/lang/Object;
Setting minSdk
below 24 (without using desugared library) this is the DEX for the test code.
.method public test(Ljava/lang/String;)V
.locals 1
.param p1, "s" # Ljava/lang/String;
.line 8
const-string v0, "test"
invoke-static {p1, v0}, Ldk/gjesse/jdk11desugaredlibrary/ObjectsTest$$ExternalSyntheticBackport0;->m(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
.line 9
new-instance v0, Ldk/gjesse/jdk11desugaredlibrary/ObjectsTest$1;
invoke-direct {v0, p0}, Ldk/gjesse/jdk11desugaredlibrary/ObjectsTest$1;-><init>(Ldk/gjesse/jdk11desugaredlibrary/ObjectsTest;)V
invoke-static {p1, v0}, Ljava/util/Objects;->requireNonNullElseGet(Ljava/lang/Object;Ljava/util/function/Supplier;)Ljava/lang/Object;
.line 15
return-void
.end method
with only Objects.requireNonNullElse
getting backported.
With desugared library enabled this change, due to two things
- desugared library adds support for
Supplier
Objects
is no longer backported, but implemented by desugared library
The "tool" com.android.tools.r8.ir.desugar.desugaredlibrary.lint.DesugaredMethodsList
is used for desugared library:
java -cp $ANDROID_HOME/cmdline-tools/latest/lib/r8.jar com.android.tools.r8.ir.desugar.desugaredlibrary.lint.DesugaredMethodsList --min-api 15 --desugared-lib META-INF/desugar/d8/desugar.json --lib $ANDROID_HOME/platforms/android-34/android.jar --desugared-lib-jar desugar_jdk_libs-2.0.4.jar | grep Objects
java/util/Objects
(META-INF/desugar/d8/desugar.json
is from desugar_jdk_libs-2.0.4.jar
is
The line java/util/Objects
indicate that all methods of Objects
are supported.
With desugared library (for any minSdk
) this is the DEX for the test code:
# virtual methods
.method public test(Ljava/lang/String;)V
.locals 1
.param p1, "s" # Ljava/lang/String;
.line 8
const-string v0, "test"
invoke-static {p1, v0}, Lj$/util/Objects;->requireNonNullElse(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
.line 9
new-instance v0, Ldk/gjesse/jdk11desugaredlibrary/ObjectsTest$1;
invoke-direct {v0, p0}, Ldk/gjesse/jdk11desugaredlibrary/ObjectsTest$1;-><init>(Ldk/gjesse/jdk11desugaredlibrary/ObjectsTest;)V
invoke-static {p1, v0}, Lj$/util/Objects;->requireNonNullElseGet(Ljava/lang/Object;Ljava/util/function/Supplier;)Ljava/lang/Object;
.line 15
return-void
.end method
pa...@google.com <pa...@google.com>
pa...@google.com <pa...@google.com> #12
Thanks for that explanation Søren.
So, when I tried this with the latest Koala canary, it looks like things are working correctly. For the following code:
Objects.requireNonNullElse
Objects.requireNonNullElseGet
Both AGP and Studio correctly will not flag the first call.
The second call is flagged as an API error. But if I turn on core library desugaring, then that warning also goes away, both in AGP and in Studio.
There is still the issue of the builtin Java 9 APIs inspection in IntelliJ -- again, not a lint bug, but it is a Studio bug. Original submitter, can you file that one? And can you confirm (if you're not seeing warnings for these) that you are in fact using library desugaring and a recent Studio?
pa...@google.com <pa...@google.com> #13
I've filed
pa...@google.com <pa...@google.com>
an...@google.com <an...@google.com> #14
thanks for filing the bug!
using library desugaring and a recent Studio?
yes I am, I have desugaring in all modules + using Koala Canary 2 now.
I will have to switch back to Jelly fish anyhow due to Koala being (to put it mildly) a runaway memory and cpu lover... which makes it nearly unusable (but that's another issue) so I might not be able to follow up on the new bug in the short term.
Description
Jetpack uses a lint check to prevent cross-group usage of the
@RequiresOptIn
annotation. The implementation is similar to@RestrictToDetector
's enforcement ofLIBRARY_GROUP
, but our check fails provisional checks on alternating runs even though they seem to be reporting as expected.We've logged some data to figure out what's going on when using
./gradlew :lint-checks:test --rerun-tasks
.For a failing run, we see:
The next run, however, passes:
The next run fails, and so on.