Enable access modification for Kotlin $lambda$ methods
This unifies the handling of Java and Kotlin lambda synthetic methods in lambda desugaring.
Change-Id: I1707484cbba23bab511ee9c04d2ca7a0e4e59778
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index da79f26..e53e662 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -351,6 +351,7 @@
public final DexString thisName = createString("this");
public final DexString javacLambdaMethodPrefix =
createString(LambdaClass.JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX);
+ public final DexString kotlinLambdaMethodIdentifier = createString("$lambda$");
public final DexString enabledFieldName = createString("ENABLED");
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index aa0355a..e5ce12a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -596,6 +596,29 @@
return true;
}
+ // Returns the index at which all remaining characters are numbers. Returns -1 if the string does
+ // not end with a number.
+ public int getNumberSuffixStartIndex() {
+ // TODO(b/146621590): This does not handle character boundaries correctly.
+ if (content.length == 1) {
+ assert isEmpty();
+ return -1;
+ }
+ int i = content.length - 2;
+ if (!isMutf8EncodedNumber(i)) {
+ return -1;
+ }
+ while (i >= 1 && isMutf8EncodedNumber(i - 1)) {
+ i--;
+ }
+ return i;
+ }
+
+ private boolean isMutf8EncodedNumber(int index) {
+ byte element = content[index];
+ return '0' <= element && element <= '9';
+ }
+
public DexString prepend(String prefix, DexItemFactory dexItemFactory) {
return prepend(dexItemFactory.createString(prefix), dexItemFactory);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 2032108..88e9773 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -210,9 +211,36 @@
/** If the lambda delegates to lambda$ method. */
public boolean delegatesToLambdaImplMethod(DexItemFactory factory) {
+ return delegatesToJavaLambdaImplMethod(factory) || delegatesToKotlinLambdaImplMethod(factory);
+ }
+
+ private boolean delegatesToJavaLambdaImplMethod(DexItemFactory factory) {
return implHandle.asMethod().getName().startsWith(factory.javacLambdaMethodPrefix);
}
+ private boolean delegatesToKotlinLambdaImplMethod(DexItemFactory factory) {
+ DexString methodName = implHandle.asMethod().getName();
+ int numberSuffixStartIndex = methodName.getNumberSuffixStartIndex();
+ if (numberSuffixStartIndex < 0) {
+ return false;
+ }
+ // Subtract the length of "$lambda$" (account for the EOF character).
+ assert factory.kotlinLambdaMethodIdentifier.content.length == 9;
+ int kotlinLambdaMethodIdentifierStartIndex = numberSuffixStartIndex - 8;
+ if (kotlinLambdaMethodIdentifierStartIndex <= 0) {
+ return false;
+ }
+ return Arrays.equals(
+ methodName.content,
+ kotlinLambdaMethodIdentifierStartIndex,
+ // To index (exclusive).
+ numberSuffixStartIndex,
+ factory.kotlinLambdaMethodIdentifier.content,
+ 0,
+ // To index (exclusive). Use the index containing the EOF character as the to index.
+ 8);
+ }
+
public void forEachErasedAndEnforcedTypes(BiConsumer<DexType, DexType> consumer) {
DexProto erasedProto = getErasedProto();
consumer.accept(erasedProto.getReturnType(), enforcedProto.getReturnType());