Inform if malformed inner-class attributes are found.
PS1 is same as go/r8g/37040.
PS3 changes compilation failure to just warning, along with more tests
regarding wrong repackaging and D8 cases.
PS6 changes warning to info, along with test infra supports.
Change-Id: I0b004294a8659df6184609e9931c7d45b61d349f
Bug: 120597515, 130706685
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index b87d13c..2a290b6 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -30,10 +30,12 @@
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FieldSignatureEquivalence;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.base.Equivalence.Wrapper;
import java.io.BufferedInputStream;
import java.io.IOException;
@@ -211,6 +213,20 @@
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
+ if (outerName != null && innerName != null) {
+ String separator =
+ DescriptorUtils.computeInnerClassSeparator(
+ outerName, name, innerName, application.options.isMinifying());
+ if (separator == null) {
+ application.options.reporter.info(
+ new StringDiagnostic(
+ StringUtils.lines(
+ "Malformed inner-class attribute:",
+ "\touterTypeInternal: " + outerName,
+ "\tinnerTypeInternal: " + name,
+ "\tinnerName: " + innerName)));
+ }
+ }
innerClasses.add(
new InnerClassAttribute(
access,
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index cc52767..725f500 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -269,7 +269,7 @@
if (outerClass != null) {
String innerClassSeparator =
computeInnerClassSeparator(
- outerClass, type, getInnerNameForType(type), appView.options());
+ outerClass, type, getInnerNameForType(type), appView.options().isMinifying());
assert innerClassSeparator != null;
state = getStateForOuterClass(outerClass, innerClassSeparator);
}
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index c996aee..4e26746 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -77,7 +77,7 @@
}
String separator =
DescriptorUtils.computeInnerClassSeparator(
- attribute.getOuter(), innerType, innerName, options);
+ attribute.getOuter(), innerType, innerName, options.isMinifying());
assert separator != null;
int index = innerTypeMapped.lastIndexOf(separator);
if (index < 0) {
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index 40c0236..d523610 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -281,7 +281,6 @@
return className.replace(JAVA_PACKAGE_SEPARATOR, DESCRIPTOR_PACKAGE_SEPARATOR);
}
-
/**
* Convert a class binary name to a descriptor.
*
@@ -310,20 +309,28 @@
}
public static String computeInnerClassSeparator(
- DexType outerClass, DexType innerClass, String innerName, InternalOptions options) {
- String outerInternalRaw = outerClass.getInternalName();
- String innerInternalRaw = innerClass.getInternalName();
+ DexType outerClass, DexType innerClass, String innerName, boolean isMinifying) {
+ return computeInnerClassSeparator(
+ outerClass.getInternalName(), innerClass.getInternalName(), innerName, isMinifying);
+ }
+
+ public static String computeInnerClassSeparator(
+ String outerDescriptor, String innerDescriptor, String innerName, boolean isMinifying) {
// outer-internal<separator>inner-name == inner-internal
- if (outerInternalRaw.length() + innerName.length() > innerInternalRaw.length()) {
+ if (outerDescriptor.length() + innerName.length() > innerDescriptor.length()) {
+ // But, if the minification is enabled, we can choose $ separator.
+ if (isMinifying) {
+ return String.valueOf(INNER_CLASS_SEPARATOR);
+ }
return null;
}
String separator =
- innerInternalRaw.substring(
- outerInternalRaw.length(), innerInternalRaw.length() - innerName.length());
+ innerDescriptor.substring(
+ outerDescriptor.length(), innerDescriptor.length() - innerName.length());
// Any non-$ separator results in a runtime exception in getCanonicalName.
if (!separator.startsWith(String.valueOf(INNER_CLASS_SEPARATOR))) {
- // But, if the minification is enabled, we can choose $ separator.
- if (options.isMinifying()) {
+ // Again, if the minification is enabled, we can choose $ separator.
+ if (isMinifying) {
return String.valueOf(INNER_CLASS_SEPARATOR);
}
return null;
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 2137c84..d63626a 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -5,7 +5,7 @@
import static com.android.tools.r8.TestBase.Backend.DEX;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.junit.Assert.assertThat;
+import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.TestBase.Backend;
@@ -177,6 +177,16 @@
return self();
}
+ public CR assertInfoMessageThatMatches(Matcher<String> matcher) {
+ getDiagnosticMessages().assertInfoMessageThatMatches(matcher);
+ return self();
+ }
+
+ public CR assertNoInfoMessageThatMatches(Matcher<String> matcher) {
+ getDiagnosticMessages().assertNoInfoMessageThatMatches(matcher);
+ return self();
+ }
+
public CR assertWarningMessageThatMatches(Matcher<String> matcher) {
getDiagnosticMessages().assertWarningMessageThatMatches(matcher);
return self();
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index d1e55f4..125329b 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -7,7 +7,6 @@
import java.util.List;
import org.hamcrest.Matcher;
-
public interface TestDiagnosticMessages {
public List<Diagnostic> getInfos();
@@ -28,6 +27,10 @@
public TestDiagnosticMessages assertErrorsCount(int count);
+ public TestDiagnosticMessages assertInfoMessageThatMatches(Matcher<String> matcher);
+
+ public TestDiagnosticMessages assertNoInfoMessageThatMatches(Matcher<String> matcher);
+
public TestDiagnosticMessages assertWarningMessageThatMatches(Matcher<String> matcher);
public TestDiagnosticMessages assertNoWarningMessageThatMatches(Matcher<String> matcher);
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
index 7ad544b..8737581 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -91,22 +91,23 @@
return this;
}
- public TestDiagnosticMessages assertWarningMessageThatMatches(Matcher<String> matcher) {
- assertNotEquals(0, getWarnings().size());
- for (int i = 0; i < getWarnings().size(); i++) {
- if (matcher.matches(getWarnings().get(i).getDiagnosticMessage())) {
+ private TestDiagnosticMessages assertMessageThatMatches(
+ List<Diagnostic> diagnostics, String tag, Matcher<String> matcher) {
+ assertNotEquals(0, diagnostics.size());
+ for (int i = 0; i < diagnostics.size(); i++) {
+ if (matcher.matches(diagnostics.get(i).getDiagnosticMessage())) {
return this;
}
}
- StringBuilder builder = new StringBuilder("No warning matches " + matcher.toString());
+ StringBuilder builder = new StringBuilder("No " + tag + " matches " + matcher.toString());
builder.append(System.lineSeparator());
if (getWarnings().size() == 0) {
- builder.append("There were no warnings.");
+ builder.append("There were no " + tag + "s.");
} else {
- builder.append("There were " + getWarnings().size() + " warnings:");
+ builder.append("There were " + diagnostics.size() + " "+ tag + "s:");
builder.append(System.lineSeparator());
- for (int i = 0; i < getWarnings().size(); i++) {
- builder.append(getWarnings().get(i).getDiagnosticMessage());
+ for (int i = 0; i < diagnostics.size(); i++) {
+ builder.append(diagnostics.get(i).getDiagnosticMessage());
builder.append(System.lineSeparator());
}
}
@@ -114,14 +115,31 @@
return this;
}
- public TestDiagnosticMessages assertNoWarningMessageThatMatches(Matcher<String> matcher) {
- assertNotEquals(0, getWarnings().size());
- for (int i = 0; i < getWarnings().size(); i++) {
- String message = getWarnings().get(i).getDiagnosticMessage();
+ private TestDiagnosticMessages assertNoMessageThatMatches(
+ List<Diagnostic> diagnostics, String tag, Matcher<String> matcher) {
+ assertNotEquals(0, diagnostics.size());
+ for (int i = 0; i < diagnostics.size(); i++) {
+ String message = diagnostics.get(i).getDiagnosticMessage();
if (matcher.matches(message)) {
- fail("The warning: \"" + message + "\" + matches " + matcher + ".");
+ fail("The " + tag + ": \"" + message + "\" + matches " + matcher + ".");
}
}
return this;
}
+
+ public TestDiagnosticMessages assertInfoMessageThatMatches(Matcher<String> matcher) {
+ return assertMessageThatMatches(getInfos(), "info", matcher);
+ }
+
+ public TestDiagnosticMessages assertNoInfoMessageThatMatches(Matcher<String> matcher) {
+ return assertNoMessageThatMatches(getInfos(), "info", matcher);
+ }
+
+ public TestDiagnosticMessages assertWarningMessageThatMatches(Matcher<String> matcher) {
+ return assertMessageThatMatches(getWarnings(), "warning", matcher);
+ }
+
+ public TestDiagnosticMessages assertNoWarningMessageThatMatches(Matcher<String> matcher) {
+ return assertNoMessageThatMatches(getWarnings(), "warning", matcher);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestDump.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestDump.java
index e3a6336..3e54cfa 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestDump.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestDump.java
@@ -36,7 +36,12 @@
MethodVisitor methodVisitor;
classWriter.visit(
- V1_8, ACC_SUPER, config.getOuterInternalType(), null, "java/lang/Object", null);
+ V1_8,
+ ACC_PUBLIC | ACC_SUPER,
+ config.getOuterInternalType(),
+ null,
+ "java/lang/Object",
+ null);
classWriter.visitSource("InnerClassNameTest.java", null);
@@ -69,7 +74,12 @@
MethodVisitor methodVisitor;
classWriter.visit(
- V1_8, ACC_SUPER, config.getInnerInternalType(), null, "java/lang/Object", null);
+ V1_8,
+ ACC_PUBLIC | ACC_SUPER,
+ config.getInnerInternalType(),
+ null,
+ "java/lang/Object",
+ null);
classWriter.visitSource("InnerClassNameTest.java", null);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
index 7d44d4b..bc6e13c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
@@ -4,13 +4,15 @@
package com.android.tools.r8.ir.optimize.reflection;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.JvmTestRunResult;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper.DexVm;
@@ -19,9 +21,7 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import java.io.IOException;
import java.util.Collection;
-import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -32,6 +32,8 @@
private static final Class<?> MAIN_CLASS = InnerClassNameTest.class;
private static final String PACKAGE = "com/android/tools/r8/ir/optimize/reflection/";
+ private static final String REPACKAGE =
+ "com/android/some/other/repackage/tools/r8/ir/optimize/reflection/";
public enum TestNamingConfig {
DEFAULT,
@@ -39,6 +41,7 @@
EMTPY_SEPARATOR,
UNDERBAR_SEPARATOR,
NON_NESTED_INNER,
+ WRONG_REPACKAGE,
OUTER_ENDS_WITH_DOLLAR,
$_$_$;
@@ -49,6 +52,7 @@
case EMTPY_SEPARATOR:
case UNDERBAR_SEPARATOR:
case NON_NESTED_INNER:
+ case WRONG_REPACKAGE:
return "OuterClass";
case OUTER_ENDS_WITH_DOLLAR:
return "OuterClass$";
@@ -61,6 +65,7 @@
public String getSeparator() {
switch (this) {
case DEFAULT:
+ case WRONG_REPACKAGE:
case OUTER_ENDS_WITH_DOLLAR:
case $_$_$:
return "$";
@@ -82,6 +87,7 @@
case $_$_$:
case UNDERBAR_SEPARATOR:
case NON_NESTED_INNER:
+ case WRONG_REPACKAGE:
case EMTPY_SEPARATOR:
return "$";
case DOLLAR2_SEPARATOR:
@@ -97,10 +103,11 @@
public String getInnerTypeRaw() {
switch (this) {
case DEFAULT:
- case OUTER_ENDS_WITH_DOLLAR:
case DOLLAR2_SEPARATOR:
case EMTPY_SEPARATOR:
case UNDERBAR_SEPARATOR:
+ case WRONG_REPACKAGE:
+ case OUTER_ENDS_WITH_DOLLAR:
case $_$_$:
return getOuterTypeRaw() + getSeparator() + getInnerClassName();
case NON_NESTED_INNER:
@@ -110,7 +117,8 @@
}
public String getInnerInternalType() {
- return PACKAGE + getInnerTypeRaw();
+ // b/130706685: Intentionally repackage inner type only.
+ return (this == WRONG_REPACKAGE ? REPACKAGE : PACKAGE) + getInnerTypeRaw();
}
public String getOuterInternalType() {
@@ -150,8 +158,47 @@
this.config = config;
}
+ private void checkWarningsAboutMalformedAttribute(TestCompileResult<?, ?> result) {
+ switch (config) {
+ case DEFAULT:
+ case OUTER_ENDS_WITH_DOLLAR:
+ case $_$_$:
+ case DOLLAR2_SEPARATOR:
+ result.assertNoMessages();
+ break;
+ case EMTPY_SEPARATOR:
+ case UNDERBAR_SEPARATOR:
+ case NON_NESTED_INNER:
+ case WRONG_REPACKAGE:
+ if (!minify) {
+ result
+ .assertInfoMessageThatMatches(containsString("Malformed inner-class attribute"))
+ .assertInfoMessageThatMatches(containsString(config.getOuterTypeRaw()))
+ .assertInfoMessageThatMatches(containsString(config.getInnerTypeRaw()));
+ } else {
+ result.assertNoMessages();
+ }
+ break;
+ default:
+ throw new Unreachable("Unexpected test configuration: " + config);
+ }
+ }
+
@Test
- public void test() throws IOException, CompilationFailedException, ExecutionException {
+ public void testD8() throws Exception {
+ assumeTrue("Only run D8 for Dex backend", parameters.isDexRuntime() && !minify);
+ D8TestCompileResult d8CompileResult =
+ testForD8()
+ .addProgramClassFileData(InnerClassNameTestDump.dump(config, parameters))
+ .setMinApi(parameters.getRuntime())
+ .compile();
+ checkWarningsAboutMalformedAttribute(d8CompileResult);
+ D8TestRunResult d8RunResult = d8CompileResult.run(parameters.getRuntime(), MAIN_CLASS);
+ d8RunResult.assertSuccessWithOutput(getExpectedNonMinified(config.getInnerClassName()));
+ }
+
+ @Test
+ public void testR8() throws Exception {
JvmTestRunResult runResult = null;
if (parameters.isCfRuntime()) {
runResult =
@@ -160,9 +207,7 @@
.run(parameters.getRuntime(), MAIN_CLASS);
}
- R8TestCompileResult r8CompileResult;
- try {
- r8CompileResult =
+ R8TestCompileResult r8CompileResult =
testForR8(parameters.getBackend())
.addKeepMainRule(MAIN_CLASS)
.addKeepRules("-keep,allowobfuscation class * { *; }")
@@ -171,11 +216,7 @@
.minification(minify)
.setMinApi(parameters.getRuntime())
.compile();
- } catch (CompilationFailedException e) {
- assertTrue("b/120597515", config == TestNamingConfig.NON_NESTED_INNER);
- assertTrue("b/120597515", minify);
- return;
- }
+ checkWarningsAboutMalformedAttribute(r8CompileResult);
CodeInspector inspector = r8CompileResult.inspector();
R8TestRunResult r8RunResult = r8CompileResult.run(parameters.getRuntime(), MAIN_CLASS);
switch (config) {
@@ -205,6 +246,8 @@
break;
case EMTPY_SEPARATOR:
case UNDERBAR_SEPARATOR:
+ case NON_NESTED_INNER:
+ case WRONG_REPACKAGE:
if (!minify
&& parameters.isCfRuntime()
&& parameters.getRuntime().asCf().getVm().lessThanOrEqual(CfVm.JDK8)) {
@@ -217,21 +260,6 @@
r8RunResult.assertSuccessWithOutput(getExpectedMinified(inspector));
}
break;
- case NON_NESTED_INNER:
- if (minify) {
- // TODO(b/120597515): better fail early while reading class file.
- throw new Unreachable("Expect to see compilation error: " + config);
- }
- if (parameters.isCfRuntime()
- && parameters.getRuntime().asCf().getVm().lessThanOrEqual(CfVm.JDK8)) {
- // Any non-$ separator results in a runtime exception in getCanonicalName.
- r8RunResult.assertFailureWithErrorThatMatches(containsString("Malformed class name"));
- } else {
- assert parameters.isDexRuntime()
- || !parameters.getRuntime().asCf().getVm().lessThanOrEqual(CfVm.JDK8);
- r8RunResult.assertSuccessWithOutput(getExpectedMinified(inspector));
- }
- break;
default:
throw new Unreachable("Unexpected test configuration: " + config);
}