Generate accessor methods for private lambda methods in R8 Bug: 179889958 Change-Id: I311f377c0b1f05f4f4091de186709d45f7f179e4
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java index 477d8d8..d0131bc 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -256,7 +256,8 @@ // Creates a delegation target for this particular lambda class. Note that we // should always be able to create targets for the lambdas we support. private Target createTarget(ProgramMethod accessedFrom) { - if (descriptor.delegatesToLambdaImplMethod()) { + if (appView.options().canAccessModifyLambdaImplementationMethods(appView) + && descriptor.delegatesToLambdaImplMethod()) { return createLambdaImplMethodTarget(accessedFrom); } @@ -339,7 +340,7 @@ assert descriptor.implHandle.type.isInvokeInstance() || descriptor.implHandle.type.isInvokeDirect(); - if (!descriptor.needsAccessor(accessedFrom)) { + if (!descriptor.needsAccessor(appView, accessedFrom)) { return new NoAccessorMethodTarget(Invoke.Type.VIRTUAL); } // We need to generate an accessor method in `accessedFrom` class/interface @@ -371,7 +372,7 @@ private Target createStaticMethodTarget(ProgramMethod accessedFrom) { assert descriptor.implHandle.type.isInvokeStatic(); - if (!descriptor.needsAccessor(accessedFrom)) { + if (!descriptor.needsAccessor(appView, accessedFrom)) { return new NoAccessorMethodTarget(Invoke.Type.STATIC); } @@ -395,7 +396,7 @@ assert implHandle != null; assert implHandle.type.isInvokeConstructor(); - if (!descriptor.needsAccessor(accessedFrom)) { + if (!descriptor.needsAccessor(appView, accessedFrom)) { return new NoAccessorMethodTarget(Invoke.Type.DIRECT); } @@ -418,14 +419,14 @@ // Create targets for interface methods. private Target createInterfaceMethodTarget(ProgramMethod accessedFrom) { assert descriptor.implHandle.type.isInvokeInterface(); - assert !descriptor.needsAccessor(accessedFrom); + assert !descriptor.needsAccessor(appView, accessedFrom); return new NoAccessorMethodTarget(Invoke.Type.INTERFACE); } private DexString generateUniqueLambdaMethodName() { return appView .dexItemFactory() - .createString(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX + descriptor.uniqueId); + .createString(LambdaRewriter.R8_LAMBDA_ACCESSOR_METHOD_PREFIX + descriptor.uniqueId); } // Represents information about the method lambda class need to delegate the call to. It may
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 f661256..b0bca4f 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
@@ -4,8 +4,11 @@ package com.android.tools.r8.ir.desugar; +import static com.android.tools.r8.ir.desugar.LambdaRewriter.JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX; + import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexDefinitionSupplier; import com.android.tools.r8.graph.DexEncodedMethod; @@ -177,10 +180,14 @@ return targetHolder == type; } + public boolean canAccessModifyLambdaImplementationMethods(AppView<?> appView) { + return appView.enableWholeProgramOptimizations(); + } + /** If the lambda delegates to lambda$ method. */ public boolean delegatesToLambdaImplMethod() { - DexString methodName = implHandle.asMethod().name; - return methodName.toString().startsWith(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX); + String methodName = implHandle.asMethod().getName().toString(); + return methodName.startsWith(JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX); } /** Is a stateless lambda, i.e. lambda does not capture any values */ @@ -189,8 +196,9 @@ } /** Checks if call site needs a accessor when referenced from `accessedFrom`. */ - boolean needsAccessor(ProgramMethod accessedFrom) { - if (delegatesToLambdaImplMethod()) { + boolean needsAccessor(AppView<?> appView, ProgramMethod accessedFrom) { + if (appView.options().canAccessModifyLambdaImplementationMethods(appView) + && delegatesToLambdaImplMethod()) { return false; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java index dbbaf99..3463db8 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -44,7 +44,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.function.Function; @@ -59,7 +58,8 @@ */ public class LambdaRewriter { - public static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$"; + public static final String JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$"; + public static final String R8_LAMBDA_ACCESSOR_METHOD_PREFIX = "access$lambda$"; public static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE"; private final AppView<?> appView; @@ -73,8 +73,6 @@ // NOTE: synchronize concurrent access on `knownLambdaClasses`. private final List<LambdaClass> knownLambdaClasses = new ArrayList<>(); - private final Map<DexMethod, Integer> methodIds = new ConcurrentHashMap<>(); - public LambdaRewriter(AppView<?> appView) { this.appView = appView; this.instanceFieldName = appView.dexItemFactory().createString(LAMBDA_INSTANCE_FIELD_NAME);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index fe679ca..97925ac 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1397,6 +1397,15 @@ } /** + * Allow access modification of synthetic lambda implementation methods in D8 to avoid generating + * an excessive amount of accessibility bridges. In R8, the lambda implementation methods are + * inlined into the synthesized accessibility bridges, thus we don't allow access modification. + */ + public boolean canAccessModifyLambdaImplementationMethods(AppView<?> appView) { + return !appView.enableWholeProgramOptimizations(); + } + + /** * Dex2Oat issues a warning for abstract methods on non-abstract classes, so we never allow this. * * <p>Note that having an invoke instruction that targets an abstract method on a non-abstract
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java index 389b0fa..e22d8df 100644 --- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -167,7 +167,7 @@ .withBuilderTransformation(ToolHelper::allowTestProguardOptions) .withBuilderTransformation( b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown())) - .withDexCheck(inspector -> checkLambdaCount(inspector, 35, "lambdadesugaringnplus")) + .withDexCheck(inspector -> checkLambdaCount(inspector, 36, "lambdadesugaringnplus")) .run(); test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods") @@ -176,7 +176,7 @@ .withBuilderTransformation(ToolHelper::allowTestProguardOptions) .withBuilderTransformation( b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown())) - .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus")) + .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaringnplus")) .run(); }
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java index a5a1a8a..6dc126c 100644 --- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java +++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -2188,7 +2188,7 @@ private static boolean isLambdaMethod(VmMirror mirror, Location location) { String methodName = mirror.getMethodName(location.classID, location.methodID); - return methodName.startsWith("lambda$"); + return methodName.startsWith("lambda$") || methodName.startsWith("$private$lambda$"); } }
diff --git a/src/test/java/com/android/tools/r8/debug/LambdaTest.java b/src/test/java/com/android/tools/r8/debug/LambdaTest.java index f9e6c34..749c4b4 100644 --- a/src/test/java/com/android/tools/r8/debug/LambdaTest.java +++ b/src/test/java/com/android/tools/r8/debug/LambdaTest.java
@@ -84,7 +84,6 @@ checkMethod(debuggeeClass, initialMethodName), checkLine(SOURCE_FILE, 20), stepInto(INTELLIJ_FILTER), - config.isCfRuntime() ? LambdaTest::doNothing : stepInto(INTELLIJ_FILTER), checkMethod(debuggeeClass, "returnOne"), checkLine(SOURCE_FILE, 28), checkNoLocal(),
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java index 7a97972..477af87 100644 --- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java +++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java
@@ -51,7 +51,7 @@ this.parameters = parameters; } - private void runDebugger(DebugTestConfig config) throws Throwable { + private void runDebugger(DebugTestConfig config, boolean isR8) throws Throwable { MethodReference main = Reference.methodFromMethod(CLASS.getMethod("main", String[].class)); Command checkThisLambda = conditional( @@ -83,7 +83,9 @@ stepInto(INTELLIJ_FILTER), checkLine(17), // When desugaring, the LambdaClass will change this to a static (later moved to companion). - checkThisLambda, + parameters.canUseDefaultAndStaticInterfaceMethods() && isR8 + ? checkThisDefaultMethod + : checkThisLambda, run()); } @@ -92,7 +94,7 @@ assumeTrue(parameters.isCfRuntime()); JvmTestBuilder builder = testForJvm().addTestClasspath(); builder.run(parameters.getRuntime(), CLASS).assertSuccessWithOutput(EXPECTED); - runDebugger(builder.debugConfig()); + runDebugger(builder.debugConfig(), false); } @Test @@ -111,7 +113,7 @@ .run(parameters.getRuntime(), CLASS) .assertSuccessWithOutput(EXPECTED) .inspect(inspector -> assertThat(inspector.clazz(CLASS), isPresent())); - runDebugger(compileResult.debugConfig()); + runDebugger(compileResult.debugConfig(), true); } @Test @@ -168,7 +170,7 @@ .run(parameters.getRuntime(), CLASS) .assertSuccessWithOutput(EXPECTED); - runDebugger(compiledResult.debugConfig()); + runDebugger(compiledResult.debugConfig(), false); Path dissasemble1 = temp.newFolder().toPath().resolve("disassemble1.txt"); Path dissasemble2 = temp.newFolder().toPath().resolve("disassemble2.txt");
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarInstanceLambdaWithReadsTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarInstanceLambdaWithReadsTest.java index bf507de..11b5e6c 100644 --- a/src/test/java/com/android/tools/r8/desugar/DesugarInstanceLambdaWithReadsTest.java +++ b/src/test/java/com/android/tools/r8/desugar/DesugarInstanceLambdaWithReadsTest.java
@@ -3,12 +3,15 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.desugar; +import static com.android.tools.r8.ir.desugar.LambdaRewriter.JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX; +import static com.android.tools.r8.ir.desugar.LambdaRewriter.R8_LAMBDA_ACCESSOR_METHOD_PREFIX; import static org.junit.Assert.assertEquals; import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; @@ -41,7 +44,7 @@ .addProgramClasses(Main.class, A.class, B.class, Consumer.class) .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutput(EXPECTED) - .inspect(this::checkJustOneLambdaImplementationMethod); + .inspect(inspector -> checkNumberOfLambdaMethods(inspector, false)); } @Test @@ -54,13 +57,27 @@ .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutput(EXPECTED) - .inspect(this::checkJustOneLambdaImplementationMethod); + .inspect(inspector -> checkNumberOfLambdaMethods(inspector, true)); } - private void checkJustOneLambdaImplementationMethod(CodeInspector inspector) { + private void checkNumberOfLambdaMethods(CodeInspector inspector, boolean isR8) { + // When generating DEX, only R8 synthesizes an accessor for the javac-generated lambda$ method. + List<FoundMethodSubject> lambdaAccessorMethods = + inspector + .clazz(Main.class) + .allMethods(m -> m.getOriginalName().startsWith(R8_LAMBDA_ACCESSOR_METHOD_PREFIX)); + assertEquals( + BooleanUtils.intValue(parameters.isDexRuntime() && isR8), lambdaAccessorMethods.size()); + + // When generating DEX, R8 will inline the javac-generated lambda$ method into the synthesized + // $r8$lambda$ accessor method. List<FoundMethodSubject> lambdaImplementationMethods = - inspector.clazz(Main.class).allMethods(m -> m.getOriginalName().startsWith("lambda$")); - assertEquals(1, lambdaImplementationMethods.size()); + inspector + .clazz(Main.class) + .allMethods(m -> m.getOriginalName().startsWith(JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX)); + assertEquals( + 1 - BooleanUtils.intValue(parameters.isDexRuntime() && isR8), + lambdaImplementationMethods.size()); } private interface Consumer {
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java index 5d84439..72549c5 100644 --- a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java +++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java
@@ -32,7 +32,7 @@ private List<String> EXPECTED_JAVAC_RESULT = ImmutableList.of("Hello from inside lambda$test$0", "Hello from inside lambda$testStatic$1"); - private List<String> EXPECTED_DESUGARED_RESULT = + private List<String> EXPECTED_D8_DESUGARED_RESULT = ImmutableList.of( "Hello from inside lambda$test$0$DesugarLambdaWithAnonymousClass$TestClass", "Hello from inside lambda$testStatic$1"); @@ -106,7 +106,7 @@ r -> r.assertSuccessWithOutputLines(EXPECTED_JAVAC_RESULT)) .applyIf( DesugarTestConfiguration::isDesugared, - r -> r.assertSuccessWithOutputLines(EXPECTED_DESUGARED_RESULT)); + r -> r.assertSuccessWithOutputLines(EXPECTED_D8_DESUGARED_RESULT)); } @Test @@ -124,8 +124,7 @@ .addKeepMainRule(TestClass.class) .run(parameters.getRuntime(), TestClass.class) .inspect(this::checkEnclosingMethod) - .assertSuccessWithOutputLines( - parameters.isCfRuntime() ? EXPECTED_JAVAC_RESULT : EXPECTED_DESUGARED_RESULT); + .assertSuccessWithOutputLines(EXPECTED_JAVAC_RESULT); assertFalse(parameters.isDexRuntime()); } catch (AssertionError e) { assertTrue(parameters.isDexRuntime());
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java index f272f26..62c36fd 100644 --- a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java +++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java
@@ -31,7 +31,7 @@ private List<String> EXPECTED_JAVAC_RESULT = ImmutableList.of("Hello from inside lambda$test$0", "Hello from inside lambda$testStatic$1"); - private List<String> EXPECTED_DESUGARED_RESULT = + private List<String> EXPECTED_D8_DESUGARED_RESULT = ImmutableList.of( "Hello from inside lambda$test$0$DesugarLambdaWithLocalClass$TestClass", "Hello from inside lambda$testStatic$1"); @@ -104,7 +104,7 @@ r -> r.assertSuccessWithOutputLines(EXPECTED_JAVAC_RESULT)) .applyIf( DesugarTestConfiguration::isDesugared, - r -> r.assertSuccessWithOutputLines(EXPECTED_DESUGARED_RESULT)); + r -> r.assertSuccessWithOutputLines(EXPECTED_D8_DESUGARED_RESULT)); } @Test @@ -120,8 +120,7 @@ .addKeepMainRule(TestClass.class) .run(parameters.getRuntime(), TestClass.class) .inspect(this::checkEnclosingMethod) - .assertSuccessWithOutputLines( - parameters.isCfRuntime() ? EXPECTED_JAVAC_RESULT : EXPECTED_JAVAC_RESULT); + .assertSuccessWithOutputLines(EXPECTED_JAVAC_RESULT); } public interface MyConsumer<T> {
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedInstanceLambdaMethodTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedInstanceLambdaMethodTest.java index b8769c3..ff70dab 100644 --- a/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedInstanceLambdaMethodTest.java +++ b/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedInstanceLambdaMethodTest.java
@@ -4,14 +4,13 @@ package com.android.tools.r8.desugar.lambdas; -import static com.android.tools.r8.ir.desugar.LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX; +import static com.android.tools.r8.ir.desugar.LambdaRewriter.JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.cf.CfVersion; -import com.android.tools.r8.utils.codeinspector.AssertUtils; import java.io.IOException; import java.lang.reflect.Method; import java.util.stream.Stream; @@ -38,7 +37,6 @@ this.parameters = parameters; } - // TODO(b/179889958): Should succeed. @Test public void testRuntime() throws Exception { testForRuntime(parameters) @@ -51,29 +49,22 @@ result -> result.assertFailureWithErrorThatThrows(NoSuchMethodError.class)); } - // TODO(b/179889958): Should succeed. @Test public void testR8() throws Exception { - AssertUtils.assertFailsCompilationIf( - parameters.isDexRuntime() && !parameters.canUseDefaultAndStaticInterfaceMethods(), - () -> - testForR8(parameters.getBackend()) - .addProgramClasses(Main.class, A.class, FunctionalInterface.class) - .addProgramClassFileData(getProgramClassFileData()) - .addKeepMainRule(Main.class) - .setMinApi(parameters.getApiLevel()) - .compile() - .run(parameters.getRuntime(), Main.class) - .applyIf( - parameters.isCfRuntime(), - result -> result.assertSuccessWithOutputLines("Hello world!", "Hello world!"), - result -> result.assertFailureWithErrorThatThrows(NoSuchMethodError.class))); + testForR8(parameters.getBackend()) + .addProgramClasses(Main.class, A.class, FunctionalInterface.class) + .addProgramClassFileData(getProgramClassFileData()) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("Hello world!", "Hello world!"); } private byte[] getProgramClassFileData() throws IOException { Method lambdaMethod = Stream.of(I.class.getDeclaredMethods()) - .filter(x -> x.getName().contains(EXPECTED_LAMBDA_METHOD_PREFIX)) + .filter(x -> x.getName().contains(JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX)) .findFirst() .get(); return transformer(I.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedStaticLambdaMethodTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedStaticLambdaMethodTest.java index e14d2f3..029f690 100644 --- a/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedStaticLambdaMethodTest.java +++ b/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedStaticLambdaMethodTest.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.desugar.lambdas; -import static com.android.tools.r8.ir.desugar.LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX; +import static com.android.tools.r8.ir.desugar.LambdaRewriter.JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; @@ -60,7 +60,7 @@ private byte[] getProgramClassFileData() throws IOException { Method lambdaMethod = Stream.of(I.class.getDeclaredMethods()) - .filter(x -> x.getName().contains(EXPECTED_LAMBDA_METHOD_PREFIX)) + .filter(x -> x.getName().contains(JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX)) .findFirst() .get(); return transformer(I.class)
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java index 2836f61..437b725 100644 --- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java +++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.naming.retrace; +import static com.android.tools.r8.ir.desugar.LambdaRewriter.R8_LAMBDA_ACCESSOR_METHOD_PREFIX; import static com.android.tools.r8.naming.retrace.StackTrace.isSame; import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileName; import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber; @@ -52,7 +53,7 @@ private int expectedActualStackTraceHeight() { // In DEX release the entire lambda is inlined. if (parameters.isDexRuntime()) { - return mode == CompilationMode.RELEASE ? 1 : 5; + return mode == CompilationMode.RELEASE ? 1 : 6; } // In CF release it is not and in debug there is no lambda desugaring thus the shorter stack. return mode == CompilationMode.RELEASE ? 2 : 4; @@ -60,19 +61,22 @@ private boolean isSynthesizedLambdaFrame(StackTraceLine line) { // TODO(141287349): The mapping should not map the external name to the internal name! - return SyntheticItemsTestUtils.isInternalLambda(Reference.classFromTypeName(line.className)); + return SyntheticItemsTestUtils.isInternalLambda(Reference.classFromTypeName(line.className)) + || line.methodName.startsWith(R8_LAMBDA_ACCESSOR_METHOD_PREFIX); } - private void checkLambdaFrame(StackTrace retracedStackTrace) { + private void checkLambdaFrames(StackTrace retracedStackTrace) { StackTrace lambdaFrames = retracedStackTrace.filter(this::isSynthesizedLambdaFrame); - assertEquals(1, lambdaFrames.size()); - if (lambdaFrames.get(0).hasLineNumber()) { - assertEquals(mode == CompilationMode.RELEASE ? 0 : 2, lambdaFrames.get(0).lineNumber); + assertEquals(2, lambdaFrames.size()); + + StackTraceLine syntheticLambdaClassFrame = lambdaFrames.get(1); + if (syntheticLambdaClassFrame.hasLineNumber()) { + assertEquals(mode == CompilationMode.RELEASE ? 0 : 2, syntheticLambdaClassFrame.lineNumber); } // Proguard retrace will take the class name until the first $ to construct the file // name, so for "-$$Lambda$...", the file name becomes "-.java". - // TODO(b/141287349): Format the class name of desugard lambda classes. - // assertEquals("-.java", lambdaFrames.get(0).fileName); + // TODO(b/141287349): Format the class name of desugared lambda classes. + // assertEquals("-.java", syntheticLambdaClassFrame.fileName); } private void checkIsSame(StackTrace actualStackTrace, StackTrace retracedStackTrace) { @@ -85,7 +89,7 @@ retracedStackTrace.filter(line -> !isSynthesizedLambdaFrame(line)), isSame(expectedStackTrace)); // Check the frame from the lambda class. - checkLambdaFrame(retracedStackTrace); + checkLambdaFrames(retracedStackTrace); } assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size()); } @@ -96,12 +100,12 @@ if (parameters.isCfRuntime()) { assertThat(retracedStackTrace, isSameExceptForFileName(expectedStackTrace)); } else { - // With the frame from the lambda class filtered out the stack trace is the same. + // With the frames from the lambda class filtered out the stack trace is the same. assertThat( retracedStackTrace.filter(line -> !isSynthesizedLambdaFrame(line)), isSameExceptForFileName(expectedStackTrace)); // Check the frame from the lambda class. - checkLambdaFrame(retracedStackTrace); + checkLambdaFrames(retracedStackTrace); } assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size()); } @@ -117,7 +121,7 @@ retracedStackTrace.filter(line -> !isSynthesizedLambdaFrame(line)), isSameExceptForFileNameAndLineNumber(expectedStackTrace)); // Check the frame from the lambda class. - checkLambdaFrame(retracedStackTrace); + checkLambdaFrames(retracedStackTrace); } assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size()); }
diff --git a/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java b/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java index 541ca47..1527d17 100644 --- a/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java +++ b/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java
@@ -49,8 +49,7 @@ .addProgramClasses(TestClass.class) .run(parameters.getRuntime(), TestClass.class); - if (isInterface - || (parameters.isCfRuntime() && parameters.getRuntime().asCf().getVm().equals(CfVm.JDK8))) { + if (isInterface || parameters.isCfRuntime(CfVm.JDK8)) { // JDK 8 allows mismatched method references in this case. result.assertSuccessWithOutput(EXPECTED); } else {