Version 2.0.15 Cherry-pick: Fix version test when we are on a release branch CL: https://r8-review.googlesource.com/47564 Cherry-pick: Reland "Do not rewrite generic signatures in target of merged classes" CL: https://r8-review.googlesource.com/47512 Cherry-pick: Do not classinline when root is a static get and is used with a monitor CL: https://r8-review.googlesource.com/47280 Cherry-pick: Allow for '.' for patterns when parsing -keepattributes CL: https://r8-review.googlesource.com/47321 Cherry-pick: Fix proguard configuration parser test to allow classname in attribute CL: https://r8-review.googlesource.com/47361 Bug: 147386014 Bug: 147411673 Bug: 147470785 Change-Id: If56d14012b6121ab76416ea7d8a8dda78d02136e
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java index 49c5a10..c47bc81 100644 --- a/src/main/java/com/android/tools/r8/Version.java +++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@ // This field is accessed from release scripts using simple pattern matching. // Therefore, changing this field could break our release scripts. - public static final String LABEL = "2.0.14"; + public static final String LABEL = "2.0.15"; private Version() { }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java index bf06bc1..8d178a3 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
@@ -21,9 +21,14 @@ */ final OptionalBool returnsReceiver; + final boolean hasMonitorOnReceiver; + public ClassInlinerEligibilityInfo( - List<Pair<Invoke.Type, DexMethod>> callsReceiver, OptionalBool returnsReceiver) { + List<Pair<Invoke.Type, DexMethod>> callsReceiver, + OptionalBool returnsReceiver, + boolean hasMonitorOnReceiver) { this.callsReceiver = callsReceiver; this.returnsReceiver = returnsReceiver; + this.hasMonitorOnReceiver = hasMonitorOnReceiver; } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java index d4d43f4..452daab 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -879,6 +879,19 @@ return null; } + if (root.isStaticGet()) { + // If we are class inlining a singleton instance from a static-get, then we don't know the + // value of the fields. + ParameterUsage receiverUsage = optimizationInfo.getParameterUsages(0); + if (receiverUsage == null || receiverUsage.hasFieldRead) { + return null; + } + if (eligibility.hasMonitorOnReceiver) { + // We will not be able to remove the monitor instruction afterwards. + return null; + } + } + // If the method returns receiver and the return value is actually // used in the code we need to make some additional checks. if (!eligibilityAcceptanceCheck.test(eligibility)) { @@ -1021,6 +1034,10 @@ } } + if (parameterUsage.isUsedInMonitor) { + return !root.isStaticGet(); + } + if (!Sets.difference(parameterUsage.ifZeroTest, ALLOWED_ZERO_TEST_TYPES).isEmpty()) { // Used in unsupported zero-check-if kinds. return false;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java index 1fb2e0c..8238723 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -166,12 +166,14 @@ List<Pair<Invoke.Type, DexMethod>> callsReceiver = new ArrayList<>(); boolean seenSuperInitCall = false; + boolean seenMonitor = false; for (Instruction insn : receiver.aliasedUsers()) { if (insn.isAssume()) { continue; } if (insn.isMonitor()) { + seenMonitor = true; continue; } @@ -246,11 +248,15 @@ return; } + boolean synchronizedVirtualMethod = + method.accessFlags.isSynchronized() && method.isVirtualMethod(); + feedback.setClassInlinerEligibility( method, new ClassInlinerEligibilityInfo( callsReceiver, - new ClassInlinerReceiverAnalysis(appView, method, code).computeReturnsReceiver())); + new ClassInlinerReceiverAnalysis(appView, method, code).computeReturnsReceiver(), + seenMonitor || synchronizedVirtualMethod)); } private void identifyParameterUsages(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java index ef87dcf..e911307 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.Invoke; import com.android.tools.r8.ir.code.InvokeMethodWithReceiver; +import com.android.tools.r8.ir.code.Monitor; import com.android.tools.r8.ir.code.Return; import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.utils.ListUtils; @@ -86,6 +87,9 @@ // If this argument is returned: return arg. public final boolean isReturned; + // If this argument is used in a monitor instruction. + public final boolean isUsedInMonitor; + ParameterUsage( int index, Set<Type> ifZeroTest, @@ -93,7 +97,8 @@ boolean hasFieldAssignment, boolean hasFieldRead, boolean isAssignedToField, - boolean isReturned) { + boolean isReturned, + boolean isUsedInMonitor) { this.index = index; this.ifZeroTest = ifZeroTest.isEmpty() ? Collections.emptySet() : ImmutableSet.copyOf(ifZeroTest); @@ -103,6 +108,7 @@ this.hasFieldRead = hasFieldRead; this.isAssignedToField = isAssignedToField; this.isReturned = isReturned; + this.isUsedInMonitor = isUsedInMonitor; } static ParameterUsage copyAndShift(ParameterUsage original, int shift) { @@ -114,7 +120,8 @@ original.hasFieldAssignment, original.hasFieldRead, original.isAssignedToField, - original.isReturned); + original.isReturned, + original.isUsedInMonitor); } public boolean notUsed() { @@ -123,7 +130,8 @@ && !hasFieldAssignment && !hasFieldRead && !isAssignedToField - && !isReturned; + && !isReturned + && !isUsedInMonitor; } } @@ -138,6 +146,7 @@ private boolean hasFieldRead = false; private boolean isAssignedToField = false; private boolean isReturned = false; + private boolean isUsedInMonitor = false; ParameterUsageBuilder(Value arg, int index) { this.arg = arg; @@ -166,6 +175,9 @@ if (instruction.isReturn()) { return note(instruction.asReturn()); } + if (instruction.isMonitor()) { + return note(instruction.asMonitor()); + } return false; } @@ -177,7 +189,8 @@ hasFieldAssignment, hasFieldRead, isAssignedToField, - isReturned); + isReturned, + isUsedInMonitor); } private boolean note(If ifInstruction) { @@ -231,5 +244,12 @@ isReturned = true; return true; } + + private boolean note(Monitor monitorInstruction) { + assert monitorInstruction.inValues().size() == 1; + assert monitorInstruction.inValues().get(0).getAliasedValue() == arg; + isUsedInMonitor = true; + return true; + } } }
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 d0240e9..d13cc3b 100644 --- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; @@ -24,12 +25,14 @@ import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode; +import com.android.tools.r8.utils.IteratorUtils; import com.android.tools.r8.utils.Timing; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -132,7 +135,15 @@ timing.end(); timing.begin("rename-generic"); - new GenericSignatureRewriter(appView, renaming).run(classes); + new GenericSignatureRewriter(appView, renaming) + .run( + new Iterable<DexProgramClass>() { + @Override + public Iterator<DexProgramClass> iterator() { + return IteratorUtils.<DexClass, DexProgramClass>filter( + classes.iterator(), DexClass::isProgramClass); + } + }); timing.end(); timing.begin("rename-arrays");
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java index 6753e06..7cb3506 100644 --- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java +++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java
@@ -9,11 +9,17 @@ */ public interface GenericSignatureAction<T> { + enum ParserPosition { + CLASS_SUPER_OR_INTERFACE_ANNOTATION, + ENCLOSING_INNER_OR_TYPE_ANNOTATION, + MEMBER_ANNOTATION + } + public void parsedSymbol(char symbol); public void parsedIdentifier(String identifier); - public T parsedTypeName(String name); + public T parsedTypeName(String name, ParserPosition isTopLevel); public T parsedInnerTypeName(T enclosingType, String name);
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java index c1f6a7d..dc0aa8c 100644 --- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java +++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.naming.signature; +import com.android.tools.r8.naming.signature.GenericSignatureAction.ParserPosition; import java.lang.reflect.GenericSignatureFormatError; import java.nio.CharBuffer; @@ -50,7 +51,7 @@ */ public class GenericSignatureParser<T> { - private final GenericSignatureAction<T> actions; + private GenericSignatureAction<T> actions; /* * Parser: @@ -110,7 +111,7 @@ try { actions.start(); setInput(signature); - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION); actions.stop(); } catch (GenericSignatureFormatError e) { throw e; @@ -141,11 +142,11 @@ parseOptFormalTypeParameters(); // SuperclassSignature ::= ClassTypeSignature. - parseClassTypeSignature(); + parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION); while (symbol > 0) { // SuperinterfaceSignature ::= ClassTypeSignature. - parseClassTypeSignature(); + parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION); } } @@ -178,28 +179,28 @@ expect(':'); if (symbol == 'L' || symbol == '[' || symbol == 'T') { - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION); } while (symbol == ':') { // InterfaceBound ::= ":" FieldTypeSignature. actions.parsedSymbol(symbol); scanSymbol(); - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION); } } - private void parseFieldTypeSignature() { + private void parseFieldTypeSignature(ParserPosition parserPosition) { // FieldTypeSignature ::= ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature. switch (symbol) { case 'L': - parseClassTypeSignature(); + parseClassTypeSignature(parserPosition); break; case '[': - // ArrayTypeSignature ::= "[" TypSignature. + // ArrayTypeSignature ::= "[" TypeSignature. actions.parsedSymbol(symbol); scanSymbol(); - updateTypeSignature(); + updateTypeSignature(parserPosition); break; case 'T': updateTypeVariableSignature(); @@ -209,7 +210,7 @@ } } - private void parseClassTypeSignature() { + private void parseClassTypeSignature(ParserPosition parserPosition) { // ClassTypeSignature ::= "L" {Ident "/"} Ident OptTypeArguments {"." Ident OptTypeArguments} // ";". actions.parsedSymbol(symbol); @@ -226,18 +227,22 @@ } qualIdent.append(this.identifier); - T parsedEnclosingType = actions.parsedTypeName(qualIdent.toString()); + T parsedEnclosingType = actions.parsedTypeName(qualIdent.toString(), parserPosition); - updateOptTypeArguments(); - - while (symbol == '.') { - // Deal with Member Classes: - actions.parsedSymbol(symbol); - scanSymbol(); - scanIdentifier(); - assert identifier != null; - parsedEnclosingType = actions.parsedInnerTypeName(parsedEnclosingType, identifier); + if (parsedEnclosingType != null) { + // We should only parse any optional type arguments and member classes if we have not merged + // the class into the current subtype. updateOptTypeArguments(); + + while (symbol == '.') { + // Deal with Member Classes. + actions.parsedSymbol(symbol); + scanSymbol(); + scanIdentifier(); + assert identifier != null; + parsedEnclosingType = actions.parsedInnerTypeName(parsedEnclosingType, identifier); + updateOptTypeArguments(); + } } actions.parsedSymbol(symbol); @@ -268,13 +273,13 @@ } else if (symbol == '+') { actions.parsedSymbol(symbol); scanSymbol(); - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION); } else if (symbol == '-') { actions.parsedSymbol(symbol); scanSymbol(); - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION); } else { - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION); } } @@ -291,7 +296,7 @@ expect(';'); } - private void updateTypeSignature() { + private void updateTypeSignature(ParserPosition parserPosition) { switch (symbol) { case 'B': case 'C': @@ -306,7 +311,7 @@ break; default: // Not an elementary type, but a FieldTypeSignature. - parseFieldTypeSignature(); + parseFieldTypeSignature(parserPosition); } } @@ -319,7 +324,7 @@ expect('('); while (symbol != ')' && (symbol > 0)) { - updateTypeSignature(); + updateTypeSignature(ParserPosition.MEMBER_ANNOTATION); } actions.parsedSymbol(symbol); @@ -336,7 +341,7 @@ if (symbol == 'T') { updateTypeVariableSignature(); } else { - parseClassTypeSignature(); + parseClassTypeSignature(ParserPosition.MEMBER_ANNOTATION); } } while (symbol == '^'); } @@ -345,7 +350,7 @@ private void updateReturnType() { // ReturnType ::= TypeSignature | "V". if (symbol != 'V') { - updateTypeSignature(); + updateTypeSignature(ParserPosition.MEMBER_ANNOTATION); } else { actions.parsedSymbol(symbol); scanSymbol();
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java index c3b7188..13c2c43 100644 --- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java +++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -10,8 +10,8 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexAnnotation; import com.android.tools.r8.graph.DexAnnotationSet; -import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexDefinition; +import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.origin.Origin; @@ -43,15 +43,16 @@ this.reporter = appView.options().reporter; } - public void run(Iterable<? extends DexClass> classes) { + public void run(Iterable<? extends DexProgramClass> classes) { final GenericSignatureCollector genericSignatureCollector = new GenericSignatureCollector(); final GenericSignatureParser<DexType> genericSignatureParser = new GenericSignatureParser<>(genericSignatureCollector); - // classes may not be the same as appInfo().classes() if applymapping is used on classpath + // Classes may not be the same as appInfo().classes() if applymapping is used on classpath // arguments. If that is the case, the ProguardMapMinifier will pass in all classes that is // either ProgramClass or has a mapping. This is then transitively called inside the // ClassNameMinifier. - for (DexClass clazz : classes) { + for (DexProgramClass clazz : classes) { + genericSignatureCollector.setCurrentClassContext(clazz); clazz.annotations = rewriteGenericSignatures( clazz.annotations, @@ -152,13 +153,22 @@ private class GenericSignatureCollector implements GenericSignatureAction<DexType> { private StringBuilder renamedSignature; + private DexProgramClass currentClassContext; - public String getRenamedSignature() { + String getRenamedSignature() { return renamedSignature.toString(); } + void setCurrentClassContext(DexProgramClass clazz) { + currentClassContext = clazz; + } + @Override public void parsedSymbol(char symbol) { + if (symbol == ';' && renamedSignature.charAt(renamedSignature.length() - 1) == ';') { + // The type was never written (maybe because it was merged with it's subtype) + return; + } renamedSignature.append(symbol); } @@ -168,13 +178,24 @@ } @Override - public DexType parsedTypeName(String name) { - DexType type = appView.dexItemFactory().createType(getDescriptorFromClassBinaryName(name)); - type = appView.graphLense().lookupType(type); + public DexType parsedTypeName(String name, ParserPosition parserPosition) { + String originalDescriptor = getDescriptorFromClassBinaryName(name); + DexType type = + appView.graphLense().lookupType(appView.dexItemFactory().createType(originalDescriptor)); if (appView.appInfo().wasPruned(type)) { type = appView.dexItemFactory().objectType; } DexString renamedDescriptor = renaming.getOrDefault(type, type.descriptor); + if (parserPosition == ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION + && currentClassContext != null) { + // We may have merged the type down to the current class type. + DexString classDescriptor = currentClassContext.type.descriptor; + if (!originalDescriptor.equals(classDescriptor.toString()) + && renamedDescriptor.equals(classDescriptor)) { + renamedSignature.deleteCharAt(renamedSignature.length() - 1); + return null; + } + } renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString())); return type; } @@ -197,6 +218,7 @@ type = appView.graphLense().lookupType(type); DexString renamedDescriptor = renaming.get(type); if (renamedDescriptor != null) { + // TODO(b/147504070): If this is a merged class equal to the class context, do not add. // Pick the renamed inner class from the fully renamed binary name. String fullRenamedBinaryName = getClassBinaryNameFromDescriptor(renamedDescriptor.toString());
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java index ab9e92f..a589f9a 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -1803,7 +1803,8 @@ codePoint -> IdentifierUtils.isDexIdentifierPart(codePoint) || codePoint == '!' - || codePoint == '*'); + || codePoint == '*' + || codePoint == '.'); } private String acceptString(Predicate<Integer> codepointAcceptor) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java index ae8eb7e..ab32bd5 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -383,7 +383,7 @@ } } if (clazz.getEnclosingMethod() != null || !clazz.getInnerClasses().isEmpty()) { - // TODO(herhut): Consider supporting merging of enclosing-method and inner-class attributes. + // TODO(b/147504070): Consider merging of enclosing-method and inner-class attributes. if (Log.ENABLED) { AbortReason.UNSUPPORTED_ATTRIBUTES.printLogMessageForClass(clazz); } @@ -440,7 +440,7 @@ return false; } if (targetClass.getEnclosingMethod() != null || !targetClass.getInnerClasses().isEmpty()) { - // TODO(herhut): Consider supporting merging of enclosing-method and inner-class attributes. + // TODO(b/147504070): Consider merging of enclosing-method and inner-class attributes. if (Log.ENABLED) { AbortReason.UNSUPPORTED_ATTRIBUTES.printLogMessageForClass(clazz); }
diff --git a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java index 7ffce35..f38c02a 100644 --- a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java +++ b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
@@ -16,7 +16,8 @@ public class IteratorUtils { - public static <T, S extends T> Iterator<S> filter(Iterator<T> iterator, Predicate<T> predicate) { + public static <T, S extends T> Iterator<S> filter( + Iterator<? extends T> iterator, Predicate<T> predicate) { return new Iterator<S>() { private S next = advance();
diff --git a/src/test/java/com/android/tools/r8/VersionTests.java b/src/test/java/com/android/tools/r8/VersionTests.java index dffaa2b..e32841c 100644 --- a/src/test/java/com/android/tools/r8/VersionTests.java +++ b/src/test/java/com/android/tools/r8/VersionTests.java
@@ -61,7 +61,13 @@ @Test public void testDevelopmentPredicate() { - assertEquals(LABEL.equals("master") || LABEL.contains("-dev"), Version.isDevelopmentVersion()); + if (LABEL.equals("master") || LABEL.contains("-dev")) { + assertTrue(Version.isDevelopmentVersion()); + } else { + // This is a release branch, but Version.isDevelopmentVersion will still return true + // since this is not the release archive with the r8-version.properties file. + assertFalse(Version.isDevelopmentVersion(LABEL, false)); + } } @Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetDirectMonitorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetDirectMonitorTest.java new file mode 100644 index 0000000..f6252ea --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetDirectMonitorTest.java
@@ -0,0 +1,114 @@ +// 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.ir.optimize.classinliner; + +import static org.junit.Assert.assertTrue; + +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.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * Currently, the class-inliner will not inline if the root is used in a monitor. This test just + * ensures, that if that ever changes, the monitor instructions will not be removed. + */ +@RunWith(Parameterized.class) +public class ClassInlinerStaticGetDirectMonitorTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public ClassInlinerStaticGetDirectMonitorTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(ClassInlinerStaticGetDirectMonitorTest.class) + .addKeepMainRule(TestClass.class) + .enableInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .noMinification() + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("20000"); + } + + private void inspect(CodeInspector inspector) { + assertTrue( + inspector + .clazz(TestClass.class) + .uniqueMethodWithName("produce1") + .streamInstructions() + .anyMatch(InstructionSubject::isMonitorEnter)); + assertTrue( + inspector + .clazz(TestClass.class) + .uniqueMethodWithName("produce2") + .streamInstructions() + .anyMatch(InstructionSubject::isMonitorExit)); + } + + static class TestClass { + + private static volatile Thread t1 = new Thread(TestClass::produce1); + private static volatile Thread t2 = new Thread(TestClass::produce2); + + @NeverInline + static void produce1() { + Container instance = Container.getInstance(); + for (int i = 0; i < 10000; i++) { + synchronized (instance) { + instance.increment(); + } + } + } + + @NeverInline + static void produce2() { + Container instance = Container.getInstance(); + for (int i = 0; i < 10000; i++) { + synchronized (instance) { + instance.increment(); + } + } + } + + public static void main(String[] args) { + t1.start(); + t2.start(); + while (t1.isAlive() || t2.isAlive()) {} + System.out.println(Container.counter); + } + } + + static class Container { + + static Container INSTANCE = new Container(); + public static int counter = 0; + + static Container getInstance() { + return INSTANCE; + } + + @NeverInline + final void increment() { + counter += 1; + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetExtraMethodMonitorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetExtraMethodMonitorTest.java new file mode 100644 index 0000000..0e0b260 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetExtraMethodMonitorTest.java
@@ -0,0 +1,105 @@ +// 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.ir.optimize.classinliner; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +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.codeinspector.CodeInspector; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * This is a reproduction of b/147411673 where we inline classes and remove monitor instructions. + */ +@RunWith(Parameterized.class) +public class ClassInlinerStaticGetExtraMethodMonitorTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public ClassInlinerStaticGetExtraMethodMonitorTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(ClassInlinerStaticGetExtraMethodMonitorTest.class) + .addKeepMainRule(TestClass.class) + .enableInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .noMinification() + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("20000"); + } + + private void inspect(CodeInspector inspector) { + assertThat(inspector.clazz(Container.class).uniqueMethodWithName("increment"), isPresent()); + } + + static class TestClass { + + private static volatile Thread t1 = new Thread(TestClass::produce1); + private static volatile Thread t2 = new Thread(TestClass::produce2); + + @NeverInline + static void produce1() { + Container instance = Container.getInstance(); + for (int i = 0; i < 10000; i++) { + synchronizeOnExtraMethod(instance); + } + } + + @NeverInline + static void produce2() { + Container instance = Container.getInstance(); + for (int i = 0; i < 10000; i++) { + synchronizeOnExtraMethod(instance); + } + } + + @NeverInline + static void synchronizeOnExtraMethod(Container container) { + synchronized (container) { + container.increment(); + } + } + + public static void main(String[] args) { + t1.start(); + t2.start(); + while (t1.isAlive() || t2.isAlive()) {} + System.out.println(Container.counter); + } + } + + static class Container { + + static Container INSTANCE = new Container(); + public static int counter = 0; + + static Container getInstance() { + return INSTANCE; + } + + @NeverInline + final void increment() { + counter += 1; + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetMonitorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetMonitorTest.java new file mode 100644 index 0000000..216f34e --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetMonitorTest.java
@@ -0,0 +1,98 @@ +// Copyright (c) 2019, 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.ir.optimize.classinliner; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +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.codeinspector.CodeInspector; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * This is a reproduction of b/147411673 where we inline classes and remove monitor instructions. + */ +@RunWith(Parameterized.class) +public class ClassInlinerStaticGetMonitorTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public ClassInlinerStaticGetMonitorTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(ClassInlinerStaticGetMonitorTest.class) + .addKeepMainRule(TestClass.class) + .enableInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .noMinification() + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("20000"); + } + + private void inspect(CodeInspector inspector) { + assertThat(inspector.clazz(Container.class).uniqueMethodWithName("increment"), isPresent()); + } + + static class TestClass { + + private static volatile Thread t1 = new Thread(TestClass::produce1); + private static volatile Thread t2 = new Thread(TestClass::produce2); + + @NeverInline + static void produce1() { + for (int i = 0; i < 10000; i++) { + Container.getInstance().increment(); + } + } + + @NeverInline + static void produce2() { + for (int i = 0; i < 10000; i++) { + Container.getInstance().increment(); + } + } + + public static void main(String[] args) { + t1.start(); + t2.start(); + while (t1.isAlive() || t2.isAlive()) {} + System.out.println(Container.counter); + } + } + + static class Container { + + static Container INSTANCE = new Container(); + public static int counter = 0; + + static Container getInstance() { + return INSTANCE; + } + + @NeverInline + final void increment() { + synchronized (this) { + counter += 1; + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java b/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java index b264ef5..4489b75 100644 --- a/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java +++ b/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java
@@ -22,6 +22,7 @@ import org.junit.Test; public class GenericSignatureParserTest extends TestBase { + private static class ReGenerateGenericSignatureRewriter implements GenericSignatureAction<String> { @@ -42,7 +43,7 @@ } @Override - public String parsedTypeName(String name) { + public String parsedTypeName(String name, ParserPosition parserPosition) { renamedSignature.append(name); return name; } @@ -389,7 +390,7 @@ } @Override - public String parsedTypeName(String name) { + public String parsedTypeName(String name, ParserPosition parserPosition) { throw exceptionSupplier.get(); } }
diff --git a/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java new file mode 100644 index 0000000..a9fc20f --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
@@ -0,0 +1,122 @@ +// 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.naming.signature; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +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.naming.signature.merging.I; +import com.android.tools.r8.naming.signature.merging.ImplI; +import com.android.tools.r8.naming.signature.merging.ImplK; +import com.android.tools.r8.naming.signature.merging.InterfaceToKeep; +import com.android.tools.r8.naming.signature.merging.J; +import com.android.tools.r8.naming.signature.merging.K; +import java.io.IOException; +import java.lang.invoke.LambdaConversionException; +import java.lang.reflect.Type; +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 SignatureOfMergedClassesTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public SignatureOfMergedClassesTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testRemovalOfMergedInterfaceOnSameClass() + throws IOException, CompilationFailedException, ExecutionException, + LambdaConversionException { + testForR8(parameters.getBackend()) + .addProgramClasses( + ImplI.class, ImplK.class, I.class, J.class, InterfaceToKeep.class, K.class, Main.class) + .addKeepMainRule(Main.class) + .addKeepClassRules(InterfaceToKeep.class) + .addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*") + .setMinApi(parameters.getApiLevel()) + .noMinification() + .addOptionsModification( + internalOptions -> { + internalOptions.enableUnusedInterfaceRemoval = false; + }) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines( + "ImplI.foo", + "ImplI: com.android.tools.r8.naming.signature.merging.InterfaceToKeep<java.lang.Void>", + "K: com.android.tools.r8.naming.signature.merging.InterfaceToKeep<java.lang.Void>", + "ImplK.foo", + "ImplK.bar", + "ImplK: interface com.android.tools.r8.naming.signature.merging.K") + .inspect( + codeInspector -> { + assertThat(codeInspector.clazz(I.class), not(isPresent())); + assertThat(codeInspector.clazz(J.class), not(isPresent())); + }); + } + + @Test + public void testKeepingOneSelfOnInterface() + throws ExecutionException, CompilationFailedException, IOException { + testForR8(parameters.getBackend()) + .addProgramClasses(Foo.class, InterfaceToKeep.class) + .addKeepMainRule(Foo.class) + .addKeepClassRules(InterfaceToKeep.class) + .addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*") + .setMinApi(parameters.getApiLevel()) + .noMinification() + .addOptionsModification( + internalOptions -> { + internalOptions.enableUnusedInterfaceRemoval = false; + }) + .run(parameters.getRuntime(), Foo.class) + .assertSuccessWithOutputLines( + "com.android.tools.r8.naming.signature.merging.InterfaceToKeep" + + "<com.android.tools.r8.naming.signature.SignatureOfMergedClassesTest$Foo>"); + } + + public static class Foo implements InterfaceToKeep<Foo> { + + public static void main(String[] args) { + for (Type genericInterface : Foo.class.getGenericInterfaces()) { + System.out.println(genericInterface); + } + } + } + + public static class Main { + + public static void main(String[] args) { + new ImplI().foo(); + for (Type genericInterface : ImplI.class.getGenericInterfaces()) { + System.out.println("ImplI: " + genericInterface); + } + for (Type genericInterface : K.class.getGenericInterfaces()) { + System.out.println("K: " + genericInterface); + } + K k = new ImplK(); + k.foo(); + k.bar(); + for (Type genericInterface : ImplK.class.getGenericInterfaces()) { + System.out.println("ImplK: " + genericInterface); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/I.java b/src/test/java/com/android/tools/r8/naming/signature/merging/I.java new file mode 100644 index 0000000..237786c --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/I.java
@@ -0,0 +1,9 @@ +// 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.naming.signature.merging; + +public interface I { + void foo(); +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/ImplI.java b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplI.java new file mode 100644 index 0000000..b842386 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplI.java
@@ -0,0 +1,13 @@ +// 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.naming.signature.merging; + +public class ImplI implements InterfaceToKeep<Void>, I { + + @Override + public void foo() { + System.out.println("ImplI.foo"); + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/ImplK.java b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplK.java new file mode 100644 index 0000000..3c23d56 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplK.java
@@ -0,0 +1,18 @@ +// 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.naming.signature.merging; + +public class ImplK implements K { + + @Override + public void foo() { + System.out.println("ImplK.foo"); + } + + @Override + public void bar() { + System.out.println("ImplK.bar"); + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/InterfaceToKeep.java b/src/test/java/com/android/tools/r8/naming/signature/merging/InterfaceToKeep.java new file mode 100644 index 0000000..6bebcd0 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/InterfaceToKeep.java
@@ -0,0 +1,7 @@ +// 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.naming.signature.merging; + +public interface InterfaceToKeep<T> {}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/J.java b/src/test/java/com/android/tools/r8/naming/signature/merging/J.java new file mode 100644 index 0000000..444a5e8 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/J.java
@@ -0,0 +1,9 @@ +// 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.naming.signature.merging; + +public interface J { + void foo(); +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/K.java b/src/test/java/com/android/tools/r8/naming/signature/merging/K.java new file mode 100644 index 0000000..4b8ed50 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/K.java
@@ -0,0 +1,9 @@ +// 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.naming.signature.merging; + +public interface K extends InterfaceToKeep<Void>, J { + void bar(); +}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java index 1a37fc5..a06dea1 100644 --- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java +++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -1407,40 +1407,6 @@ } @Test - public void parseInvalidKeepattributes_className() throws Exception { - List<String> classNames = ImmutableList.of("androidx.annotation.Keep", "**.Keep", "K*<1>p"); - Path proguardConfig; - for (String className : classNames) { - reset(); - proguardConfig = writeTextToTempFile("-keepattributes " + className + ",*Annotations*"); - try { - parser.parse(proguardConfig); - fail("Expect to fail due to unsupported attribute."); - } catch (AbortException e) { - checkDiagnostics( - handler.errors, - proguardConfig, - 1, - className.contains(".") ? className.indexOf('.') + 17 : className.length() + 13, - "Unexpected attribute"); - } - reset(); - proguardConfig = writeTextToTempFile("-keepattributes *Annotations*," + className); - try { - parser.parse(proguardConfig); - fail("Expect to fail due to unsupported attribute."); - } catch (AbortException e) { - checkDiagnostics( - handler.errors, - proguardConfig, - 1, - className.contains(".") ? className.indexOf('.') + 31 : className.length() + 27, - "Unexpected attribute"); - } - } - } - - @Test public void parseUseUniqueClassMemberNames() throws IOException { Path proguardConfig = writeTextToTempFile("-useuniqueclassmembernames"); new ProguardConfigurationParser(new DexItemFactory(), reporter).parse(proguardConfig);
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java new file mode 100644 index 0000000..97763b1 --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java
@@ -0,0 +1,112 @@ +// 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.shaking.attributes; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestRuntime; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.FieldSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; +import java.util.concurrent.ExecutionException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepAttributesDotsTest extends TestBase { + + private final TestParameters parameters; + private final String keepAttributes; + + @Parameterized.Parameters(name = "-keepattributes {1}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withNoneRuntime().build(), + new String[] {".", "...", "XYZ,..", "XYZ,..,A.B"}); + } + + public KeepAttributesDotsTest(TestParameters parameters, String keepAttributes) { + this.parameters = parameters; + this.keepAttributes = keepAttributes; + } + + @Test + public void testProguard() throws ExecutionException, CompilationFailedException, IOException { + testForProguard() + .addProgramClassesAndInnerClasses(Main.class) + .addKeepAllClassesRule() + .addKeepAttributes(keepAttributes) + .addKeepRules("-dontwarn com.android.tools.r8.shaking.attributes.*") + .run(TestRuntime.getCheckedInJdk8(), Main.class) + .assertSuccessWithOutputLines("Hello World!") + .inspect(this::inspect); + } + + @Test + public void testR8() throws IOException, CompilationFailedException, ExecutionException { + testForR8(Backend.CF) + .addProgramClassesAndInnerClasses(Main.class) + .addKeepAttributes(keepAttributes) + .addKeepAllClassesRule() + .run(TestRuntime.getCheckedInJdk8(), Main.class) + .assertSuccessWithOutputLines("Hello World!") + .inspect(this::inspect); + } + + private void inspect(CodeInspector inspector) { + ClassSubject clazz = inspector.clazz(Main.class); + assertTrue(clazz.getDexClass().annotations.isEmpty()); + MethodSubject main = clazz.uniqueMethodWithName("main"); + assertTrue(main.getMethod().annotations.isEmpty()); + FieldSubject field = clazz.uniqueFieldWithName("field"); + assertTrue(field.getField().annotations.isEmpty()); + assertTrue(clazz.getDexClass().sourceFile == null || clazz.getDexClass().sourceFile.size == 0); + assertNull(main.getLineNumberTable()); + assertTrue(main.getLocalVariableTable().isEmpty()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.METHOD}) + public @interface MethodRuntimeAnnotation {} + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.METHOD}) + public @interface MethodCompileTimeAnnotation {} + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE}) + public @interface ClassRuntimeAnnotation {} + + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.TYPE}) + public @interface ClassCompileTimeAnnotation {} + + @ClassCompileTimeAnnotation + @ClassRuntimeAnnotation + public static class Main { + + public static class Inner<T> {} + + public Inner<Boolean> field; + + @MethodCompileTimeAnnotation + @MethodRuntimeAnnotation + public static void main(String[] args) { + System.out.println("Hello World!"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java index 4aec544..1a4dcc16 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -423,7 +423,7 @@ } @Override - public String parsedTypeName(String name) { + public String parsedTypeName(String name, ParserPosition parserPosition) { String type = name; if (obfuscatedToOriginalMapping != null) { String original = mapType(obfuscatedToOriginalMapping, name);