blob: 018c6185c133a53d413bb55a1fa7c85d8c019197 [file] [log] [blame]
// Copyright (c) 2022, 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.naming.retrace.StackTrace.isSame;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.MethodTransformer;
import com.android.tools.r8.utils.IntBox;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.objectweb.asm.Label;
@RunWith(Parameterized.class)
public class AsmZeroLineEntryRegressionTest extends TestBase {
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withDefaultCfRuntime().build();
}
public AsmZeroLineEntryRegressionTest(TestParameters parameters) {
this.parameters = parameters;
}
/**
* Reference test showing JVM printing of zero-line valued entries in the line number table.
*
* <p>JVM spec defines line values to be u2 (unsigned 2-byte) values without other restrictions.
*/
@Test
public void testReference() throws Exception {
testForJvm()
.addProgramClassFileData(getClassWithZeroLineEntry())
.run(parameters.getRuntime(), TestClass.class)
.assertFailureWithErrorThatThrows(RuntimeException.class)
.inspectOriginalStackTrace(st -> checkLineNumber(st, 0));
}
/**
* Regression test for ASM stripping out zero-line entries.
*
* <p>See b/260389461
*/
@Test
public void testAsmIdentity() throws Exception {
testForJvm()
.addProgramClassFileData(
getClassAfterAsmIdentity(
getClassWithZeroLineEntry(), Reference.classFromClass(TestClass.class)))
.run(parameters.getRuntime(), TestClass.class)
.assertFailureWithErrorThatThrows(RuntimeException.class)
// If this becomes zero ASM has been updated to not assign line zero a special meaning.
.inspectOriginalStackTrace(st -> checkLineNumber(st, 1));
}
private void checkLineNumber(StackTrace st, int lineNumber) {
assertThat(
st,
isSame(
StackTrace.builder()
.add(
StackTraceLine.builder()
.setFileName(getClass().getSimpleName() + ".java")
.setClassName(TestClass.class.getTypeName())
.setMethodName("main")
.setLineNumber(lineNumber)
.build())
.build()));
}
private byte[] getClassAfterAsmIdentity(byte[] bytes, ClassReference clazz) {
return transformer(bytes, clazz)
// Add the identity line transform. If no method transformation is added then ASM will not
// interpret the line table and will retain the zero.
.addMethodTransformer(
new MethodTransformer() {
@Override
public void visitLineNumber(int line, Label start) {
super.visitLineNumber(line, start);
}
})
.transform();
}
private byte[] getClassWithZeroLineEntry() throws IOException {
IntBox nextLine = new IntBox(1);
return transformer(TestClass.class)
.addMethodTransformer(
new MethodTransformer() {
@Override
public void visitLineNumber(int line, Label start) {
if (getContext().getReference().getMethodName().equals("main")) {
int newLine = nextLine.get();
if (newLine > 0) {
nextLine.decrement(1);
}
super.visitLineNumber(newLine, start);
} else {
super.visitLineNumber(line, start);
}
}
})
.transform();
}
static class TestClass {
public static void main(String[] args) {
System.out.println("Hello, world!");
throw new RuntimeException("BOO!");
}
}
}