| // Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| package com.android.tools.r8.debuginfo; | |
| import static com.android.tools.r8.Collectors.toSingle; | |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame; | |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack; | |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; | |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isTopOfStackTrace; | |
| import static junit.framework.TestCase.assertEquals; | |
| import static junit.framework.TestCase.assertNotNull; | |
| import static junit.framework.TestCase.assertNull; | |
| import static org.hamcrest.CoreMatchers.containsString; | |
| import static org.hamcrest.CoreMatchers.not; | |
| import static org.hamcrest.MatcherAssert.assertThat; | |
| import com.android.tools.r8.CompilationFailedException; | |
| import com.android.tools.r8.TestBase; | |
| import com.android.tools.r8.TestParameters; | |
| import com.android.tools.r8.TestParametersCollection; | |
| import com.android.tools.r8.ToolHelper.DexVm.Version; | |
| import com.android.tools.r8.references.Reference; | |
| import com.android.tools.r8.retrace.RetraceMethodResult; | |
| import com.android.tools.r8.shaking.ProguardKeepAttributes; | |
| import com.android.tools.r8.utils.AndroidApiLevel; | |
| import com.android.tools.r8.utils.codeinspector.ClassSubject; | |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; | |
| import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; | |
| import com.android.tools.r8.utils.codeinspector.InstructionSubject; | |
| import com.android.tools.r8.utils.codeinspector.Matchers.LinePosition; | |
| import com.android.tools.r8.utils.codeinspector.MethodSubject; | |
| import com.google.common.collect.ImmutableList; | |
| import java.io.IOException; | |
| import java.util.concurrent.ExecutionException; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.Parameterized; | |
| import org.junit.runners.Parameterized.Parameters; | |
| @RunWith(Parameterized.class) | |
| public class DexPcWithDebugInfoForOverloadedMethodsTestRunner extends TestBase { | |
| private static final String FILENAME_INLINE = "InlineFunction.kt"; | |
| private static final Class<?> MAIN = DexPcWithDebugInfoForOverloadedMethodsTest.class; | |
| private static final int MINIFIED_LINE_POSITION = 6; | |
| private static final String EXPECTED = "java.lang.RuntimeException: overloaded(String)42"; | |
| private final TestParameters parameters; | |
| @Parameters(name = "{0}") | |
| public static TestParametersCollection data() { | |
| return getTestParameters() | |
| .withDexRuntimesStartingFromIncluding(Version.V8_1_0) | |
| .withApiLevelsStartingAtIncluding(AndroidApiLevel.O) | |
| .build(); | |
| } | |
| public DexPcWithDebugInfoForOverloadedMethodsTestRunner(TestParameters parameters) { | |
| this.parameters = parameters; | |
| } | |
| @Test | |
| public void testEmittedDebugInfoForOverloads() | |
| throws ExecutionException, CompilationFailedException, IOException, NoSuchMethodException { | |
| testForR8(parameters.getBackend()) | |
| .addProgramClasses(MAIN) | |
| .addKeepMainRule(MAIN) | |
| .addKeepMethodRules(MAIN, "void overloaded(...)") | |
| .addKeepAttributeLineNumberTable() | |
| .addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE) | |
| .setMinApi(parameters.getApiLevel()) | |
| .addOptionsModification( | |
| internalOptions -> { | |
| // TODO(b/37830524): Remove when activated. | |
| internalOptions.enablePcDebugInfoOutput = true; | |
| }) | |
| .run(parameters.getRuntime(), MAIN) | |
| .assertFailureWithErrorThatMatches(containsString(EXPECTED)) | |
| .inspectOriginalStackTrace( | |
| (stackTrace, inspector) -> { | |
| assertEquals(MAIN.getTypeName(), stackTrace.get(0).className); | |
| assertEquals("overloaded", stackTrace.get(0).methodName); | |
| assertThat(stackTrace.get(0).fileName, not("Unknown Source")); | |
| inspect(inspector); | |
| }) | |
| .inspectStackTrace( | |
| (stackTrace, codeInspector) -> { | |
| MethodSubject throwingSubject = | |
| codeInspector.clazz(MAIN).method("void", "overloaded", "java.lang.String"); | |
| assertThat(throwingSubject, isPresent()); | |
| LinePosition inlineStack = | |
| LinePosition.stack( | |
| LinePosition.create( | |
| Reference.methodFromMethod( | |
| MAIN.getDeclaredMethod("inlinee", String.class)), | |
| MINIFIED_LINE_POSITION, | |
| 11, | |
| FILENAME_INLINE), | |
| LinePosition.create( | |
| Reference.methodFromMethod( | |
| MAIN.getDeclaredMethod("overloaded", String.class)), | |
| MINIFIED_LINE_POSITION, | |
| 20, | |
| FILENAME_INLINE)); | |
| RetraceMethodResult retraceResult = | |
| throwingSubject | |
| .streamInstructions() | |
| .filter(InstructionSubject::isThrow) | |
| .collect(toSingle()) | |
| .retraceLinePosition(codeInspector.retrace()); | |
| assertThat(retraceResult, isInlineFrame()); | |
| assertThat(retraceResult, isInlineStack(inlineStack)); | |
| assertThat( | |
| retraceResult, | |
| isTopOfStackTrace( | |
| stackTrace, | |
| ImmutableList.of(MINIFIED_LINE_POSITION, MINIFIED_LINE_POSITION))); | |
| }); | |
| } | |
| private void inspect(CodeInspector inspector) { | |
| ClassSubject clazz = inspector.clazz(MAIN); | |
| assertThat(clazz, isPresent()); | |
| assertEquals(3, clazz.allMethods().size()); | |
| for (FoundMethodSubject method : clazz.allMethods()) { | |
| if (method.getOriginalName().equals("main")) { | |
| assertNull(method.getMethod().getCode().asDexCode().getDebugInfo()); | |
| } else { | |
| assertEquals("overloaded", method.getOriginalName()); | |
| assertNotNull(method.getMethod().getCode().asDexCode().getDebugInfo()); | |
| } | |
| } | |
| } | |
| } |