Merge commit 'f360ce2486bd287908762b33c335c9b4f8a8802a' into dev-release
diff --git a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java index 7199b77..12e7b48 100644 --- a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java +++ b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
@@ -4,12 +4,22 @@ package com.android.tools.r8; +import com.android.tools.r8.references.MethodReference; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.Reporter; @Keep public class AssertionsConfiguration { - /** The possible transformations of the javac generated assertion code during compilation. */ + /** + * The simple transformations of the javac generated assertion code during compilation (see {@link + * AssertionsConfiguration.Builder#setTransformation(AssertionTransformation)}). For configuring + * the transformation to invoke an assertion handler use {@link + * AssertionsConfiguration.Builder#setAssertionHandler(MethodReference)}. + * + * @deprecated As of version 3.3 this enum should not be used. + */ + @Deprecated @Keep public enum AssertionTransformation { /** Unconditionally enable the javac generated assertion code. */ @@ -30,20 +40,51 @@ } private final AssertionTransformation transformation; + private final MethodReference assertionHandler; private final AssertionTransformationScope scope; private final String value; AssertionsConfiguration( - AssertionTransformation transformation, AssertionTransformationScope scope, String value) { + AssertionTransformation transformation, + MethodReference assertionHandler, + AssertionTransformationScope scope, + String value) { this.transformation = transformation; + this.assertionHandler = assertionHandler; this.scope = scope; this.value = value; + assert BooleanUtils.xor(transformation != null, assertionHandler != null); } + public boolean isCompileTimeEnabled() { + return transformation == AssertionTransformation.ENABLE; + } + + public boolean isCompileTimeDisabled() { + return transformation == AssertionTransformation.DISABLE; + } + + public boolean isPassthrough() { + return transformation == AssertionTransformation.PASSTHROUGH; + } + + public boolean isAssertionHandler() { + return assertionHandler != null; + } + + /** + * @deprecated As of version 3.3, use one of {@link #isCompileTimeEnabled()} ()}, {@link + * #isCompileTimeDisabled()} ()} or {@link #isPassthrough()} ()}. + */ + @Deprecated public AssertionTransformation getTransformation() { return transformation; } + public MethodReference getAssertionHandler() { + return assertionHandler; + } + public AssertionTransformationScope getScope() { return scope; } @@ -66,6 +107,7 @@ public static class Builder { Reporter reporter; private AssertionTransformation transformation; + private MethodReference assertionHandler; private AssertionTransformationScope scope; private String value; @@ -73,10 +115,17 @@ this.reporter = reporter; } - /** Set how to handle javac generated assertion code. */ + /** + * Set how to handle javac generated assertion code. + * + * @deprecated As of version 3.3, use one of {@link #setCompileTimeDisable()}, {@link + * #setCompileTimeDisable()} or {@link #setPassthrough()} ()}. + */ + @Deprecated public AssertionsConfiguration.Builder setTransformation( AssertionTransformation transformation) { this.transformation = transformation; + this.assertionHandler = null; return this; } @@ -84,6 +133,13 @@ * Unconditionally enable javac generated assertion code in all packages and classes. This * corresponds to passing <code>-enableassertions</code> or <code>-ea</code> to the java CLI. */ + public AssertionsConfiguration.Builder setCompileTimeEnable() { + setTransformation(AssertionTransformation.ENABLE); + return this; + } + + /** @deprecated As of version 3.3, replaced by {@link #setCompileTimeEnable()} ()} */ + @Deprecated public AssertionsConfiguration.Builder setEnable() { setTransformation(AssertionTransformation.ENABLE); return this; @@ -93,6 +149,13 @@ * Disable the javac generated assertion code in all packages and classes. This corresponds to * passing <code>-disableassertions</code> or <code>-da</code> to the java CLI. */ + public AssertionsConfiguration.Builder setCompileTimeDisable() { + setTransformation(AssertionTransformation.DISABLE); + return this; + } + + /** @deprecated As of version 3.3, replaced by {@link #setCompileTimeDisable()} */ + @Deprecated public AssertionsConfiguration.Builder setDisable() { setTransformation(AssertionTransformation.DISABLE); return this; @@ -104,6 +167,18 @@ return this; } + /** + * Rewrite the throwing of <code>java.lang.AssertionError</code> to call the supplied method + * <code>assertionHandler</code>. The method must be a reference to a static method taking one + * argument of type <code>java.lang.AssertionError</code>. After the assertion handler as been + * called, the code continues as if assertions where disabled. + */ + public AssertionsConfiguration.Builder setAssertionHandler(MethodReference assertionHandler) { + this.transformation = null; + this.assertionHandler = assertionHandler; + return this; + } + public AssertionsConfiguration.Builder setScopeAll() { this.scope = AssertionTransformationScope.ALL; this.value = null; @@ -144,19 +219,20 @@ /** Build and return the {@link AssertionsConfiguration}. */ public AssertionsConfiguration build() { - if (transformation == null) { - reporter.error("No transformation specified for building AccertionConfiguration"); + if (transformation == null && assertionHandler == null) { + reporter.error( + "No transformation or assertion handler specified for building AssertionConfiguration"); } if (scope == null) { - reporter.error("No scope specified for building AccertionConfiguration"); + reporter.error("No scope specified for building AssertionConfiguration"); } if (scope == AssertionTransformationScope.PACKAGE && value == null) { - reporter.error("No package name specified for building AccertionConfiguration"); + reporter.error("No package name specified for building AssertionConfiguration"); } if (scope == AssertionTransformationScope.CLASS && value == null) { - reporter.error("No class name specified for building AccertionConfiguration"); + reporter.error("No class name specified for building AssertionConfiguration"); } - return new AssertionsConfiguration(transformation, scope, value); + return new AssertionsConfiguration(transformation, assertionHandler, scope, value); } /** @@ -174,14 +250,25 @@ * * <pre> * D8Command command = D8Command.builder() - * .addAssertionsConfiguration(builder -> builder.setEnabled().setScopeAll().build()) + * .addAssertionsConfiguration( + * builder -> builder.setCompileTimeEnable().setScopeAll().build()) * ... * .build(); * </pre> */ + public static AssertionsConfiguration compileTimeEnableAllAssertions( + AssertionsConfiguration.Builder builder) { + return builder.setCompileTimeEnable().setScopeAll().build(); + } + + /** + * @deprecated As of version 3.3, replaced by {@link #compileTimeEnableAllAssertions(Builder)} + * ()} + */ + @Deprecated public static AssertionsConfiguration enableAllAssertions( AssertionsConfiguration.Builder builder) { - return builder.setEnable().setScopeAll().build(); + return compileTimeEnableAllAssertions(builder); } /** @@ -190,7 +277,8 @@ * * <pre> * D8Command command = D8Command.builder() - * .addAssertionsConfiguration(AssertionsConfiguration.Builder::disableAllAssertions) + * .addAssertionsConfiguration( + * AssertionsConfiguration.Builder::compileTimeDisableAllAssertions) * ... * .build(); * </pre> @@ -199,14 +287,25 @@ * * <pre> * D8Command command = D8Command.builder() - * .addAssertionsConfiguration(builder -> builder.setDisabled().setScopeAll().build()) + * .addAssertionsConfiguration( + * builder -> builder.setCompileTimeDisabled().setScopeAll().build()) * ... * .build(); * </pre> */ + public static AssertionsConfiguration compileTimeDisableAllAssertions( + AssertionsConfiguration.Builder builder) { + return builder.setCompileTimeDisable().setScopeAll().build(); + } + + /** + * @deprecated As of version 3.3, replaced by {@link #compileTimeDisableAllAssertions(Builder)} + * ()} + */ + @Deprecated public static AssertionsConfiguration disableAllAssertions( AssertionsConfiguration.Builder builder) { - return builder.setDisable().setScopeAll().build(); + return compileTimeDisableAllAssertions(builder); } /**
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java index ad3e92b..cd0bc2f 100644 --- a/src/main/java/com/android/tools/r8/D8.java +++ b/src/main/java/com/android/tools/r8/D8.java
@@ -210,7 +210,7 @@ clazz -> { ProgramMethod classInitializer = clazz.getProgramClassInitializer(); if (classInitializer != null) { - analysis.processNewlyLiveMethod(classInitializer, clazz); + analysis.processNewlyLiveMethod(classInitializer, clazz, null); } }, executor);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index a12020f..8dd34a8 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -507,7 +507,11 @@ assert internal.assertionsConfiguration == null; internal.assertionsConfiguration = new AssertionConfigurationWithDefault( - AssertionTransformation.DISABLE, getAssertionsConfiguration()); + AssertionsConfiguration.builder(getReporter()) + .setTransformation(AssertionTransformation.DISABLE) + .setScopeAll() + .build(), + getAssertionsConfiguration()); internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java index 41f4030..26c99ce 100644 --- a/src/main/java/com/android/tools/r8/L8Command.java +++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -202,7 +202,11 @@ assert internal.assertionsConfiguration == null; internal.assertionsConfiguration = new AssertionConfigurationWithDefault( - AssertionTransformation.DISABLE, getAssertionsConfiguration()); + AssertionsConfiguration.builder(getReporter()) + .setTransformation(AssertionTransformation.DISABLE) + .setScopeAll() + .build(), + getAssertionsConfiguration()); if (!DETERMINISTIC_DEBUGGING) { assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java index b51ee2e..bf04680 100644 --- a/src/main/java/com/android/tools/r8/R8Command.java +++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -485,9 +485,6 @@ } ProguardConfiguration.Builder configurationBuilder = parser.getConfigurationBuilder(); configurationBuilder.setForceProguardCompatibility(forceProguardCompatibility); - if (InternalOptions.shouldEnableKeepRuleSynthesisForRecompilation()) { - configurationBuilder.enableKeepRuleSynthesisForRecompilation(); - } if (proguardConfigurationConsumerForTesting != null) { proguardConfigurationConsumerForTesting.accept(configurationBuilder); @@ -921,9 +918,13 @@ assert internal.assertionsConfiguration == null; internal.assertionsConfiguration = new AssertionConfigurationWithDefault( - getProgramConsumer() instanceof ClassFileConsumer - ? AssertionTransformation.PASSTHROUGH - : AssertionTransformation.DISABLE, + AssertionsConfiguration.builder(getReporter()) + .setTransformation( + getProgramConsumer() instanceof ClassFileConsumer + ? AssertionTransformation.PASSTHROUGH + : AssertionTransformation.DISABLE) + .setScopeAll() + .build(), getAssertionsConfiguration()); // TODO(b/171552739): Enable class merging for CF. When compiling libraries, we need to be
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java deleted file mode 100644 index 058201f..0000000 --- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java +++ /dev/null
@@ -1,26 +0,0 @@ -// Copyright (c) 2021, 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.androidapi; - -import com.android.tools.r8.graph.DexField; -import com.android.tools.r8.graph.DexMethod; -import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.utils.AndroidApiLevel; -import java.util.function.BiConsumer; - -/** - * This is an interface for all generated classes from api-versions.xml for building a database from - * a serialized hashed format. - */ -public interface AndroidApiForHashingClass { - - DexType getType(); - - AndroidApiLevel getApiLevel(); - - void visitMethodsWithApiLevels(BiConsumer<DexMethod, AndroidApiLevel> consumer); - - void visitFieldsWithApiLevels(BiConsumer<DexField, AndroidApiLevel> consumer); -}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingReference.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingReference.java new file mode 100644 index 0000000..726b1a0 --- /dev/null +++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingReference.java
@@ -0,0 +1,33 @@ +// 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.androidapi; + +import com.android.tools.r8.graph.DexReference; +import com.android.tools.r8.utils.AndroidApiLevel; + +/** This interface is used to add additional known references to the api database. */ +class AndroidApiForHashingReference { + + private final DexReference reference; + + private final AndroidApiLevel apiLevel; + + private AndroidApiForHashingReference(DexReference reference, AndroidApiLevel apiLevel) { + this.reference = reference; + this.apiLevel = apiLevel; + } + + static AndroidApiForHashingReference create(DexReference reference, AndroidApiLevel apiLevel) { + return new AndroidApiForHashingReference(reference, apiLevel); + } + + DexReference getReference() { + return reference; + } + + AndroidApiLevel getApiLevel() { + return apiLevel; + } +}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java new file mode 100644 index 0000000..03727d7 --- /dev/null +++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
@@ -0,0 +1,103 @@ +// 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.androidapi; + +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexReference; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.utils.AndroidApiLevel; +import java.util.function.BiConsumer; + +class AndroidApiLevelDatabaseHelper { + + static void visitAdditionalKnownApiReferences( + DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) { + // StringBuilder and StringBuffer lack api definitions for the exact same methods in + // api-versions.xml. See b/216587554 for related error. + for (DexType type : new DexType[] {factory.stringBuilderType, factory.stringBufferType}) { + apiLevelConsumer.accept( + factory.createMethod(type, factory.createProto(factory.intType), "capacity"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, factory.createProto(factory.intType, factory.intType), "codePointAt"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, factory.createProto(factory.intType, factory.intType), "codePointBefore"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, + factory.createProto(factory.intType, factory.intType, factory.intType), + "codePointCount"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, factory.createProto(factory.voidType, factory.intType), "ensureCapacity"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, + factory.createProto( + factory.voidType, + factory.intType, + factory.intType, + factory.charArrayType, + factory.intType), + "getChars"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, factory.createProto(factory.intType, factory.stringType), "indexOf"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, + factory.createProto(factory.intType, factory.stringType, factory.intType), + "indexOf"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, factory.createProto(factory.intType, factory.stringType), "lastIndexOf"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, + factory.createProto(factory.intType, factory.stringType, factory.intType), + "lastIndexOf"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, + factory.createProto(factory.intType, factory.intType, factory.intType), + "offsetByCodePoints"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, + factory.createProto(factory.voidType, factory.intType, factory.charType), + "setCharAt"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, factory.createProto(factory.voidType, factory.intType), "setLength"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, factory.createProto(factory.stringType, factory.intType), "substring"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod( + type, + factory.createProto(factory.stringType, factory.intType, factory.intType), + "substring"), + AndroidApiLevel.B); + apiLevelConsumer.accept( + factory.createMethod(type, factory.createProto(factory.voidType), "trimToSize"), + AndroidApiLevel.B); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java index bedc254..f5343da 100644 --- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java +++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
@@ -41,24 +41,21 @@ private final Map<DexReference, AndroidApiLevel> ambiguousCache = new IdentityHashMap<>(); public AndroidApiLevelHashingDatabaseImpl( - List<AndroidApiForHashingClass> predefinedApiTypeLookup) { + List<AndroidApiForHashingReference> predefinedApiTypeLookup) { loadData(); predefinedApiTypeLookup.forEach( - apiClass -> { - DexType type = apiClass.getType(); - lookupNonAmbiguousCache.put(type.hashCode(), null); - ambiguousCache.put(type, apiClass.getApiLevel()); - apiClass.visitMethodsWithApiLevels( - (method, apiLevel) -> { - lookupNonAmbiguousCache.put(method.hashCode(), null); - ambiguousCache.put(method, apiLevel); - }); - apiClass.visitFieldsWithApiLevels( - (field, apiLevel) -> { - lookupNonAmbiguousCache.put(field.hashCode(), null); - ambiguousCache.put(field, apiLevel); - }); + predefinedApiReference -> { + int hashCode = predefinedApiReference.getReference().hashCode(); + // Do not use computeIfAbsent since a return value of null implies the key should not be + // inserted. + if (!lookupNonAmbiguousCache.containsKey(hashCode)) { + lookupNonAmbiguousCache.put(hashCode, null); + ambiguousCache.put( + predefinedApiReference.getReference(), predefinedApiReference.getApiLevel()); + } }); + assert predefinedApiTypeLookup.stream() + .allMatch(added -> added.getApiLevel().isEqualTo(lookupApiLevel(added.getReference()))); } private void loadData() {
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java index b4609ce..2e2aa7a 100644 --- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java +++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -11,8 +11,10 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification; import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.ConsumerUtils; import com.google.common.collect.ImmutableList; import java.util.List; +import java.util.function.BiConsumer; public class AndroidApiReferenceLevelCache { @@ -25,7 +27,7 @@ private AndroidApiReferenceLevelCache( AppView<?> appView, AndroidApiLevelCompute apiLevelCompute, - List<AndroidApiForHashingClass> predefinedApiTypeLookupForHashing) { + List<AndroidApiForHashingReference> predefinedApiTypeLookupForHashing) { this.appView = appView; this.apiLevelCompute = apiLevelCompute; factory = appView.dexItemFactory(); @@ -37,11 +39,15 @@ public static AndroidApiReferenceLevelCache create( AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) { assert appView.options().apiModelingOptions().enableApiCallerIdentification; - ImmutableList.Builder<AndroidApiForHashingClass> builder = ImmutableList.builder(); + ImmutableList.Builder<AndroidApiForHashingReference> builder = ImmutableList.builder(); + BiConsumer<DexReference, AndroidApiLevel> addItemToList = + ConsumerUtils.andThen(AndroidApiForHashingReference::create, builder::add); + AndroidApiLevelDatabaseHelper.visitAdditionalKnownApiReferences( + appView.dexItemFactory(), addItemToList); appView .options() .apiModelingOptions() - .visitMockedApiLevelsForReferences(appView.dexItemFactory(), builder::add); + .visitMockedApiLevelsForReferences(appView.dexItemFactory(), addItemToList); return new AndroidApiReferenceLevelCache(appView, apiLevelCompute, builder.build()); }
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java index ae4700f..f9b13a4 100644 --- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java +++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -116,7 +116,7 @@ public ApiReferenceStubber(AppView<? extends AppInfoWithClassHierarchy> appView) { this.appView = appView; - apiLevelCompute = AndroidApiLevelCompute.create(appView); + apiLevelCompute = appView.apiLevelCompute(); desugaredLibraryConfiguration = appView.options().desugaredLibrarySpecification; }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java index 07ebb29..f35751b 100644 --- a/src/main/java/com/android/tools/r8/graph/CfCode.java +++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -19,11 +19,14 @@ import com.android.tools.r8.cf.code.CfReturnVoid; import com.android.tools.r8.cf.code.CfTryCatch; import com.android.tools.r8.code.Base5Format; +import com.android.tools.r8.code.CfOrDexInstruction; import com.android.tools.r8.errors.InvalidDebugInfoException; import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo; import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.MemberType; import com.android.tools.r8.ir.code.NumberGenerator; @@ -150,6 +153,7 @@ private final List<LocalVariableInfo> localVariables; private StackMapStatus stackMapStatus = StackMapStatus.NOT_VERIFIED; private final com.android.tools.r8.position.Position diagnosticPosition; + private final BytecodeMetadata<CfInstruction> metadata; public CfCode( DexType originalHolder, int maxStack, int maxLocals, List<CfInstruction> instructions) { @@ -187,6 +191,26 @@ List<CfTryCatch> tryCatchRanges, List<LocalVariableInfo> localVariables, com.android.tools.r8.position.Position diagnosticPosition) { + this( + originalHolder, + maxStack, + maxLocals, + instructions, + tryCatchRanges, + localVariables, + diagnosticPosition, + BytecodeMetadata.empty()); + } + + public CfCode( + DexType originalHolder, + int maxStack, + int maxLocals, + List<CfInstruction> instructions, + List<CfTryCatch> tryCatchRanges, + List<LocalVariableInfo> localVariables, + com.android.tools.r8.position.Position diagnosticPosition, + BytecodeMetadata<CfInstruction> metadata) { this.originalHolder = originalHolder; this.maxStack = maxStack; this.maxLocals = maxLocals; @@ -194,6 +218,7 @@ this.tryCatchRanges = tryCatchRanges; this.localVariables = localVariables; this.diagnosticPosition = diagnosticPosition; + this.metadata = metadata; } @Override @@ -207,6 +232,20 @@ } @Override + public BytecodeMetadata<CfInstruction> getMetadata() { + return metadata; + } + + @Override + public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) { + return getMetadata(instruction.asCfInstruction()); + } + + public BytecodeInstructionMetadata getMetadata(CfInstruction instruction) { + return metadata.getMetadata(instruction); + } + + @Override public StructuralMapping<CfCode> getStructuralMapping() { throw new Unreachable(); }
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java index e727373..f8d5917 100644 --- a/src/main/java/com/android/tools/r8/graph/Code.java +++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.dex.MixedSectionCollection; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.NumberGenerator; import com.android.tools.r8.ir.code.Position; @@ -31,6 +32,10 @@ + getClass().getCanonicalName()); } + public BytecodeMetadata<? extends CfOrDexInstruction> getMetadata() { + return null; + } + public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) { return null; }
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java index 7689694..a44fc1d 100644 --- a/src/main/java/com/android/tools/r8/graph/DexCode.java +++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -123,6 +123,11 @@ } @Override + public BytecodeMetadata<Instruction> getMetadata() { + return metadata; + } + + @Override public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) { return getMetadata(instruction.asDexInstruction()); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java index 0f27bda..0beeb2f 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java +++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -291,17 +291,16 @@ assert code.getDebugInfo().isPcBasedInfo(); PcBasedDebugInfo pcBasedDebugInfo = code.getDebugInfo().asPcBasedInfo(); // Generate a line event at each throwing instruction. - List<DexDebugEvent> events = new ArrayList<>(code.instructions.length + 1); - events.add(factory.zeroChangeDefaultEvent); + List<DexDebugEvent> events = new ArrayList<>(code.instructions.length); int pc = 0; int delta = 0; for (Instruction instruction : code.instructions) { - delta += instruction.getSize(); if (instruction.canThrow()) { DexDebugEventBuilder.addDefaultEventWithAdvancePcIfNecessary(delta, delta, events, factory); pc += delta; delta = 0; } + delta += instruction.getSize(); } assert pc + delta - ArrayUtils.last(code.instructions).getSize() <= pcBasedDebugInfo.maxPc; return new EventBasedDebugInfo(
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java index 33f6e4d..ccbb86f 100644 --- a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java +++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
@@ -25,22 +25,24 @@ } @Override - public void notifyMarkFieldAsReachable(ProgramField field) { + public void notifyMarkFieldAsReachable(ProgramField field, EnqueuerWorklist worklist) { processSignature(field, field.getContext()); } @Override - public void processNewlyLiveField(ProgramField field, ProgramDefinition context) { + public void processNewlyLiveField( + ProgramField field, ProgramDefinition context, EnqueuerWorklist worklist) { processSignature(field, context); } @Override - public void notifyMarkMethodAsTargeted(ProgramMethod method) { + public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) { processSignature(method, method.getContext()); } @Override - public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) { + public void processNewlyLiveMethod( + ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) { processSignature(method, context); }
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java index 28ab2ca..745c558 100644 --- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java +++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
@@ -6,6 +6,7 @@ import java.util.Set; import java.util.function.BiConsumer; +import java.util.function.Consumer; /** * Provides immutable access to {@link ObjectAllocationInfoCollectionImpl}, which stores the set of @@ -28,6 +29,8 @@ boolean isImmediateInterfaceOfInstantiatedLambda(DexProgramClass clazz); + void forEachInstantiatedLambdaInterfaces(Consumer<DexType> consumer); + ObjectAllocationInfoCollection rewrittenWithLens( DexDefinitionSupplier definitions, GraphLens lens); }
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java index aa5e4c8..bcad963 100644 --- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java +++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -242,6 +242,11 @@ return instantiatedLambdas.keySet(); } + @Override + public void forEachInstantiatedLambdaInterfaces(Consumer<DexType> consumer) { + getInstantiatedLambdaInterfaces().forEach(consumer); + } + public void removeAllocationsForPrunedItems(PrunedItems prunedItems) { Set<DexType> removedClasses = prunedItems.getRemovedClasses(); if (removedClasses.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java index 972c83a..e6a6cba 100644 --- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java +++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -234,11 +234,7 @@ return new Builder(); } - public RewrittenTypeInfo(DexType oldType, DexType newType) { - this(oldType, newType, null, null); - } - - public RewrittenTypeInfo( + private RewrittenTypeInfo( DexType oldType, DexType newType, DexType castType, SingleValue singleValue) { this.castType = castType; this.oldType = oldType;
diff --git a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java index 0800ede..2382abc 100644 --- a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java +++ b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
@@ -33,9 +33,15 @@ private final Map<DexType, TypeInfo> typeInfo; - private SubtypingInfo(Map<DexType, TypeInfo> typeInfo, Map<DexType, Set<DexType>> subtypeMap) { + private final DexItemFactory factory; + + private SubtypingInfo( + Map<DexType, TypeInfo> typeInfo, + Map<DexType, Set<DexType>> subtypeMap, + DexItemFactory factory) { this.typeInfo = typeInfo; this.subtypeMap = subtypeMap; + this.factory = factory; } public static SubtypingInfo create(AppView<? extends AppInfoWithClassHierarchy> appView) { @@ -46,12 +52,12 @@ return create(appInfo.app().asDirect().allClasses(), appInfo); } - private static SubtypingInfo create( - Collection<DexClass> classes, DexDefinitionSupplier definitions) { + public static SubtypingInfo create( + Collection<? extends DexClass> classes, DexDefinitionSupplier definitions) { Map<DexType, TypeInfo> typeInfo = new ConcurrentHashMap<>(); Map<DexType, Set<DexType>> subtypeMap = new IdentityHashMap<>(); populateSubtypeMap(classes, subtypeMap, typeInfo, definitions); - return new SubtypingInfo(typeInfo, subtypeMap); + return new SubtypingInfo(typeInfo, subtypeMap, definitions.dexItemFactory()); } private static void populateSuperType( @@ -111,7 +117,7 @@ } private static void populateSubtypeMap( - Collection<DexClass> classes, + Collection<? extends DexClass> classes, Map<DexType, Set<DexType>> map, Map<DexType, TypeInfo> typeInfo, DexDefinitionSupplier definitionSupplier) { @@ -225,6 +231,13 @@ return ImmutableList.of(); } + public void forAllInterfaceRoots(Consumer<DexType> fn) { + Iterables.filter( + getTypeInfo(factory.objectType).directSubtypes, + subtype -> getTypeInfo(subtype).isInterface()) + .forEach(fn); + } + private static class TypeInfo { private final DexType type;
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java index 86c7e2d..6e6088a 100644 --- a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java +++ b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
@@ -14,6 +14,7 @@ import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry; +import com.android.tools.r8.shaking.EnqueuerWorklist; public class ApiModelAnalysis extends EnqueuerAnalysis { @@ -28,17 +29,20 @@ } @Override - public void processNewlyLiveField(ProgramField field, ProgramDefinition context) { + public void processNewlyLiveField( + ProgramField field, ProgramDefinition context, EnqueuerWorklist worklist) { computeAndSetApiLevelForDefinition(field); } @Override - public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) { + public void processNewlyLiveMethod( + ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) { computeAndSetApiLevelForDefinition(method); } @Override - public void processTracedCode(ProgramMethod method, DefaultEnqueuerUseRegistry registry) { + public void processTracedCode( + ProgramMethod method, DefaultEnqueuerUseRegistry registry, EnqueuerWorklist worklist) { assert registry.getMaxApiReferenceLevel().isGreaterThanOrEqualTo(minApiLevel); if (appView.options().apiModelingOptions().tracedMethodApiLevelCallback != null) { appView @@ -52,17 +56,18 @@ } @Override - public void notifyMarkMethodAsTargeted(ProgramMethod method) { + public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) { computeAndSetApiLevelForDefinition(method); } @Override - public void notifyMarkFieldAsReachable(ProgramField field) { + public void notifyMarkFieldAsReachable(ProgramField field, EnqueuerWorklist worklist) { computeAndSetApiLevelForDefinition(field); } @Override - public void notifyMarkVirtualDispatchTargetAsLive(LookupTarget target) { + public void notifyMarkVirtualDispatchTargetAsLive( + LookupTarget target, EnqueuerWorklist worklist) { target.accept( this::computeAndSetApiLevelForDefinition, lookupLambdaTarget -> { @@ -71,7 +76,8 @@ } @Override - public void notifyFailedMethodResolutionTarget(DexEncodedMethod method) { + public void notifyFailedMethodResolutionTarget( + DexEncodedMethod method, EnqueuerWorklist worklist) { // We may not trace into failed resolution targets. method.setApiLevelForCode(ComputedApiLevel.unknown()); }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java index 45bff3f..a6045e4 100644 --- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java +++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -22,6 +22,7 @@ import com.android.tools.r8.graph.ProgramDefinition; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; +import com.android.tools.r8.shaking.EnqueuerWorklist; import com.google.common.collect.ImmutableList; import java.util.List; import org.objectweb.asm.Opcodes; @@ -39,7 +40,8 @@ } @Override - public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) { + public void processNewlyLiveMethod( + ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) { DexEncodedMethod definition = method.getDefinition(); if (definition.isClassInitializer()) { Code code = definition.getCode();
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java index ceb7e92..eef04d0 100644 --- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java +++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -18,27 +18,33 @@ public abstract class EnqueuerAnalysis { /** Called when a class is found to be instantiated. */ - public void processNewlyInstantiatedClass(DexProgramClass clazz, ProgramMethod context) {} + public void processNewlyInstantiatedClass( + DexProgramClass clazz, ProgramMethod context, EnqueuerWorklist worklist) {} /** Called when a class is found to be live. */ public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {} /** Called when a field is found to be live. */ - public void processNewlyLiveField(ProgramField field, ProgramDefinition context) {} + public void processNewlyLiveField( + ProgramField field, ProgramDefinition context, EnqueuerWorklist worklist) {} /** Called when a method is found to be live. */ - public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {} + public void processNewlyLiveMethod( + ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {} /** Called when a method's code has been processed by the registry. */ - public void processTracedCode(ProgramMethod method, DefaultEnqueuerUseRegistry registry) {} + public void processTracedCode( + ProgramMethod method, DefaultEnqueuerUseRegistry registry, EnqueuerWorklist worklist) {} - public void notifyMarkMethodAsTargeted(ProgramMethod method) {} + public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) {} - public void notifyMarkFieldAsReachable(ProgramField field) {} + public void notifyMarkFieldAsReachable(ProgramField field, EnqueuerWorklist worklist) {} - public void notifyMarkVirtualDispatchTargetAsLive(LookupTarget target) {} + public void notifyMarkVirtualDispatchTargetAsLive( + LookupTarget target, EnqueuerWorklist worklist) {} - public void notifyFailedMethodResolutionTarget(DexEncodedMethod method) {} + public void notifyFailedMethodResolutionTarget( + DexEncodedMethod method, EnqueuerWorklist worklist) {} /** * Called when the Enqueuer reaches a fixpoint. This may happen multiple times, since each
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java index cc74d21..0d19daa 100644 --- a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java +++ b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.shaking.Enqueuer; +import com.android.tools.r8.shaking.EnqueuerWorklist; import java.util.IdentityHashMap; import java.util.Map; @@ -63,7 +64,8 @@ } @Override - public void processNewlyInstantiatedClass(DexProgramClass clazz, ProgramMethod context) { + public void processNewlyInstantiatedClass( + DexProgramClass clazz, ProgramMethod context, EnqueuerWorklist worklist) { DexType key = clazz.type; DexType objectType = appView.dexItemFactory().objectType; if (context == null) {
diff --git a/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java index ad7dee4..f335b4c 100644 --- a/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java +++ b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java
@@ -60,5 +60,10 @@ public BytecodeMetadata<I> build() { return backing.isEmpty() ? empty() : new BytecodeMetadata<>(backing); } + + public boolean verifyNoMetadata(Instruction irInstruction) { + assert bytecodeMetadataProvider.getMetadata(irInstruction) == null; + return true; + } } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java index bd5b1da..62dda05 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
@@ -59,6 +59,14 @@ cache.put(type, true); return true; } + if (clazz.hasClassInitializer() + && clazz.getClassInitializer().getOptimizationInfo().mayHaveSideEffects() + && clazz.getMethodCollection().hasVirtualMethods(method -> !method.isAbstract())) { + // Require interfaces that trigger class initialization side effects to be implement by all + // group members. + cache.put(type, true); + return true; + } for (DexType parentType : clazz.getInterfaces()) { if (computeInterfaceHasDirectOrIndirectRuntimeTypeCheck(parentType)) { cache.put(type, true);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java index d0cfd52..b0ddb4a 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -132,7 +132,8 @@ * ProtoMessageInfo} object, and create a mapping from the holder to it. */ @Override - public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) { + public void processNewlyLiveMethod( + ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) { if (references.isFindLiteExtensionByNumberMethod(method.getReference())) { findLiteExtensionByNumberMethods.add(method); return;
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java index 45fa439..0e09c04 100644 --- a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java +++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
@@ -42,7 +42,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfNop()); + builder.add(new CfNop(), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java index bf545a0..a4be195 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java +++ b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
@@ -165,6 +165,6 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfArithmeticBinop(getCfOpcode(), type)); + builder.add(new CfArithmeticBinop(getCfOpcode(), type), this); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java b/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java index 9140e87..eb20267 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java +++ b/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java
@@ -24,6 +24,12 @@ return inValues.get(INDEX_INDEX); } + public int getIndexOrDefault(int defaultValue) { + return index().isConstant() + ? index().getConstInstruction().asConstInstruction().asConstNumber().getIntValue() + : defaultValue; + } + @Override public boolean isArrayAccess() { return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java index e1bb5bf..d51e318 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java +++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -176,7 +176,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfArrayLoad(type)); + builder.add(new CfArrayLoad(type), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java index 4f0995b..3835b3d 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java +++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -116,7 +116,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfArrayLength()); + builder.add(new CfArrayLength(), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java index 4108175..9994dde 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java +++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -213,7 +213,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfArrayStore(type)); + builder.add(new CfArrayStore(type), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java index e34ab51..864e37e 100644 --- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java +++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -264,7 +264,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfCheckCast(type)); + builder.add(new CfCheckCast(type), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Cmp.java b/src/main/java/com/android/tools/r8/ir/code/Cmp.java index 2b44f93..a7b25a3 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Cmp.java +++ b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
@@ -231,7 +231,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfCmp(bias, type)); + builder.add(new CfCmp(bias, type), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java index 93cefd2..716396d 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -197,7 +197,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfConstClass(clazz)); + builder.add(new CfConstClass(clazz), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java index 3973d6e..35cef20 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -63,7 +63,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfConstMethodHandle(methodHandle)); + builder.add(new CfConstMethodHandle(methodHandle), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java index 6f153a6..64359c8 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -63,7 +63,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfConstMethodType(methodType)); + builder.add(new CfConstMethodType(methodType), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java index b849f75..4111b17 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -173,9 +173,9 @@ @Override public void buildCf(CfBuilder builder) { if (outType().isObject()) { - builder.add(new CfConstNull()); + builder.add(new CfConstNull(), this); } else { - builder.add(new CfConstNumber(value, outType())); + builder.add(new CfConstNumber(value, outType()), this); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java index f873fd8..a3efba8 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -144,7 +144,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfConstString(value)); + builder.add(new CfConstString(value), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java index 6817315..ad9be48 100644 --- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java +++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -69,7 +69,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfStore(outType(), builder.getLocalRegister(outValue()))); + builder.add(new CfStore(outType(), builder.getLocalRegister(outValue())), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java index d4d76da..e5bdb6d 100644 --- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java +++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -86,7 +86,7 @@ public void buildCf(CfBuilder builder) { assert getPosition().isSome() && !getPosition().isSyntheticPosition(); // All redundant debug positions are removed. Remaining ones must force a pc advance. - builder.add(new CfNop()); + builder.add(new CfNop(), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java index 4c8c646..4dc3985 100644 --- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java +++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -139,7 +139,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfDexItemBasedConstString(item, nameComputationInfo)); + builder.add(new CfDexItemBasedConstString(item, nameComputationInfo), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup.java b/src/main/java/com/android/tools/r8/ir/code/Dup.java index 3bdc40c..2c9c3be 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Dup.java +++ b/src/main/java/com/android/tools/r8/ir/code/Dup.java
@@ -69,9 +69,9 @@ @Override public void buildCf(CfBuilder builder) { if (this.inValues.get(0).getType().isWidePrimitive()) { - builder.add(new CfStackInstruction(Opcode.Dup2)); + builder.add(new CfStackInstruction(Opcode.Dup2), this); } else { - builder.add(new CfStackInstruction(Opcode.Dup)); + builder.add(new CfStackInstruction(Opcode.Dup), this); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup2.java b/src/main/java/com/android/tools/r8/ir/code/Dup2.java index a01b789..7eff1c4 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Dup2.java +++ b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
@@ -86,7 +86,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfStackInstruction(Opcode.Dup2)); + builder.add(new CfStackInstruction(Opcode.Dup2), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Goto.java b/src/main/java/com/android/tools/r8/ir/code/Goto.java index dde7f0d..025dec0 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Goto.java +++ b/src/main/java/com/android/tools/r8/ir/code/Goto.java
@@ -118,7 +118,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfGoto(builder.getLabel(getTarget()))); + builder.add(new CfGoto(builder.getLabel(getTarget())), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java index c562fe7..468c7bf 100644 --- a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java +++ b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
@@ -65,6 +65,10 @@ return get(Opcodes.AND); } + public boolean mayHaveArrayGet() { + return get(Opcodes.ARRAY_GET); + } + public boolean mayHaveArrayLength() { return get(Opcodes.ARRAY_LENGTH); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java index 62cb5a2..ce75f73 100644 --- a/src/main/java/com/android/tools/r8/ir/code/If.java +++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -268,11 +268,11 @@ public void buildCf(CfBuilder builder) { ValueType ifType = inValues.get(0).outType(); if (inValues.size() == 1) { - builder.add(new CfIf(type, ifType, builder.getLabel(getTrueTarget()))); + builder.add(new CfIf(type, ifType, builder.getLabel(getTrueTarget())), this); return; } assert inValues.size() == 2; assert inValues.get(0).outType() == inValues.get(1).outType(); - builder.add(new CfIfCmp(type, ifType, builder.getLabel(getTrueTarget()))); + builder.add(new CfIfCmp(type, ifType, builder.getLabel(getTrueTarget())), this); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Inc.java b/src/main/java/com/android/tools/r8/ir/code/Inc.java index 3d10cc4..de99253 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Inc.java +++ b/src/main/java/com/android/tools/r8/ir/code/Inc.java
@@ -83,17 +83,21 @@ @Override public void buildCf(CfBuilder builder) { + // Check that this instruction does not have any metadata attached, as it might not materialize + // as an iinc in CfCode. + assert builder.verifyNoMetadata(this); Value inValue = inValues.get(0); int inRegister = builder.getLocalRegister(inValue); int outRegister = builder.getLocalRegister(outValue); if (inRegister == outRegister) { - builder.add(new CfIinc(inRegister, increment)); + builder.add(new CfIinc(inRegister, increment), this); } else { assert inValue.outType() == ValueType.INT; - builder.add(new CfLoad(ValueType.INT, inRegister)); - builder.add(new CfConstNumber(increment, ValueType.INT)); - builder.add(new CfArithmeticBinop(Opcode.Add, NumericType.INT)); - builder.add(new CfStore(ValueType.INT, outRegister)); + builder.add( + new CfLoad(ValueType.INT, inRegister), + new CfConstNumber(increment, ValueType.INT), + new CfArithmeticBinop(Opcode.Add, NumericType.INT), + new CfStore(ValueType.INT, outRegister)); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InitClass.java b/src/main/java/com/android/tools/r8/ir/code/InitClass.java index aceeab3..80351a6 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InitClass.java +++ b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
@@ -76,7 +76,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfInitClass(clazz)); + builder.add(new CfInitClass(clazz), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java index 347ca58..0bc0830 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java +++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -200,7 +200,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfInstanceFieldRead(getField(), builder.resolveField(getField()))); + builder.add(new CfInstanceFieldRead(getField(), builder.resolveField(getField())), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java index 0530403..123eb45 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java +++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -109,7 +109,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfInstanceOf(type)); + builder.add(new CfInstanceOf(type), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java index 95086dc..13cba81 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java +++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -241,7 +241,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfInstanceFieldWrite(getField(), builder.resolveField(getField()))); + builder.add(new CfInstanceFieldWrite(getField(), builder.resolveField(getField())), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java b/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java index e0e3cdc..601f67f 100644 --- a/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java +++ b/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
@@ -311,12 +311,12 @@ } } assert index == numberOfKeys(); - builder.add(new CfSwitch(Kind.TABLE, fallthroughLabel, new int[] {min}, labels)); + builder.add(new CfSwitch(Kind.TABLE, fallthroughLabel, new int[] {min}, labels), this); } else { for (int index : targetBlockIndices()) { labels.add(builder.getLabel(successors.get(index))); } - builder.add(new CfSwitch(Kind.LOOKUP, fallthroughLabel, this.keys, labels)); + builder.add(new CfSwitch(Kind.LOOKUP, fallthroughLabel, this.keys, labels), this); } } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java index 508b115..1296742 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -147,7 +147,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfInvokeDynamic(getCallSite())); + builder.add(new CfInvokeDynamic(getCallSite()), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java index 0f2e98a..062220b 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -161,7 +161,8 @@ @Override public void buildCf(CfBuilder builder) { builder.add( - new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface)); + new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface), + this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java index 6412e17..153ca1b 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -128,7 +128,8 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEINTERFACE, getInvokedMethod(), true)); + builder.add( + new CfInvoke(org.objectweb.asm.Opcodes.INVOKEINTERFACE, getInvokedMethod(), true), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java index 7163fdd..63df06a 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -104,7 +104,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfMultiANewArray(type, arguments().size())); + builder.add(new CfMultiANewArray(type, arguments().size()), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java index 425262b..ad8c087 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -104,7 +104,7 @@ // To translate InvokePolymorphic back into InvokeVirtual, use the original prototype // that is stored in getProto(). DexMethod method = factory.createMethod(dexMethod.holder, getProto(), dexMethod.name); - builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, method, false)); + builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, method, false), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java index f1f86b3..7aa20ad 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -155,7 +155,8 @@ @Override public void buildCf(CfBuilder builder) { builder.add( - new CfInvoke(org.objectweb.asm.Opcodes.INVOKESTATIC, getInvokedMethod(), isInterface)); + new CfInvoke(org.objectweb.asm.Opcodes.INVOKESTATIC, getInvokedMethod(), isInterface), + this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java index 0a0461d..630e177 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -84,7 +84,8 @@ @Override public void buildCf(CfBuilder builder) { builder.add( - new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface)); + new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface), + this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java index 9179014..b5933de 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -157,7 +157,8 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, getInvokedMethod(), false)); + builder.add( + new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, getInvokedMethod(), false), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Load.java b/src/main/java/com/android/tools/r8/ir/code/Load.java index eee3bef..fe565bc 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Load.java +++ b/src/main/java/com/android/tools/r8/ir/code/Load.java
@@ -76,7 +76,7 @@ @Override public void buildCf(CfBuilder builder) { Value value = src(); - builder.add(new CfLoad(value.outType(), builder.getLocalRegister(value))); + builder.add(new CfLoad(value.outType(), builder.getLocalRegister(value)), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java index 267db9b..b647ecf 100644 --- a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java +++ b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
@@ -141,6 +141,6 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfLogicalBinop(getCfOpcode(), type)); + builder.add(new CfLogicalBinop(getCfOpcode(), type), this); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java index f779651..648c9fd 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Monitor.java +++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -137,7 +137,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfMonitor(type)); + builder.add(new CfMonitor(type), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Neg.java b/src/main/java/com/android/tools/r8/ir/code/Neg.java index ad0886a..459c8c5 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Neg.java +++ b/src/main/java/com/android/tools/r8/ir/code/Neg.java
@@ -108,6 +108,6 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfNeg(type)); + builder.add(new CfNeg(type), this); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java index 3be3d3d..3d6fb7d 100644 --- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java +++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -152,7 +152,7 @@ @Override public void buildCf(CfBuilder builder) { assert type.isArrayType(); - builder.add(new CfNewArray(type)); + builder.add(new CfNewArray(type), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java index 024f6f2..3aff847 100644 --- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java +++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -125,7 +125,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfNew(clazz)); + builder.add(new CfNew(clazz), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java index 33829cd..b56600f 100644 --- a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java +++ b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
@@ -132,7 +132,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfNewUnboxedEnum(clazz, ordinal)); + builder.add(new CfNewUnboxedEnum(clazz, ordinal), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java index e6b6f8d..5f3b592 100644 --- a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java +++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -159,7 +159,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfNumberConversion(from, to)); + builder.add(new CfNumberConversion(from, to), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Pop.java b/src/main/java/com/android/tools/r8/ir/code/Pop.java index a2f872e..bf8ee2d 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Pop.java +++ b/src/main/java/com/android/tools/r8/ir/code/Pop.java
@@ -79,7 +79,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(CfStackInstruction.popType(inValues.get(0).outType())); + builder.add(CfStackInstruction.popType(inValues.get(0).outType()), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java index 5de6390..653e20e 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Position.java +++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -153,14 +153,14 @@ public Position getOutermostCallerMatchingOrElse( Predicate<Position> predicate, Position defaultValue) { - return getOutermostCallerMatchingOrElse(predicate, defaultValue, false); + Position outerMostMatching = getOutermostCallerMatching(predicate, false); + return outerMostMatching == null ? defaultValue : outerMostMatching; } - private Position getOutermostCallerMatchingOrElse( - Predicate<Position> predicate, Position defaultValue, boolean isCallerPosition) { + private Position getOutermostCallerMatching( + Predicate<Position> predicate, boolean isCallerPosition) { if (hasCallerPosition()) { - Position position = - getCallerPosition().getOutermostCallerMatchingOrElse(predicate, defaultValue, true); + Position position = getCallerPosition().getOutermostCallerMatching(predicate, true); if (position != null) { return position; } @@ -168,7 +168,7 @@ if (isCallerPosition && predicate.test(this)) { return this; } - return defaultValue; + return null; } public Position withOutermostCallerPosition(Position newOutermostCallerPosition) { @@ -180,14 +180,15 @@ .build(); } - public Position replaceOutermostCallerPosition(Position newOutermostCallerPosition) { - if (!hasCallerPosition()) { - return newOutermostCallerPosition; + public Position replacePosition(Position originalPosition, Position newPosition) { + if (this == originalPosition) { + return newPosition; } - return builderWithCopy() - .setCallerPosition( - getCallerPosition().replaceOutermostCallerPosition(newOutermostCallerPosition)) - .build(); + return hasCallerPosition() + ? builderWithCopy() + .setCallerPosition(callerPosition.replacePosition(originalPosition, newPosition)) + .build() + : this; } @Override @@ -324,7 +325,8 @@ .setLine(line) .setFile(file) .setMethod(method) - .setCallerPosition(callerPosition); + .setCallerPosition(callerPosition) + .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe()); } @Override @@ -389,7 +391,11 @@ @Override public PositionBuilder<?, ?> builderWithCopy() { - return builder().setLine(line).setMethod(method).setCallerPosition(callerPosition); + return builder() + .setLine(line) + .setMethod(method) + .setCallerPosition(callerPosition) + .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe()); } @Override @@ -442,7 +448,11 @@ @Override public PositionBuilder<?, ?> builderWithCopy() { - return builder().setLine(line).setMethod(method).setCallerPosition(callerPosition); + return builder() + .setLine(line) + .setMethod(method) + .setCallerPosition(callerPosition) + .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe()); } @Override @@ -516,7 +526,8 @@ .setMethod(method) .setCallerPosition(callerPosition) .setOutlineCallee(outlineCallee) - .setIsOutline(isOutline); + .setIsOutline(isOutline) + .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe()); outlinePositions.forEach(outlineCallerPositionBuilder::addOutlinePosition); return outlineCallerPositionBuilder; }
diff --git a/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java b/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java index 00df561..a2949d0 100644 --- a/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java +++ b/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java
@@ -73,7 +73,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfRecordFieldValues(fields)); + builder.add(new CfRecordFieldValues(fields), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java index e28ee89..1ec1a8e 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Return.java +++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -132,7 +132,8 @@ @Override public void buildCf(CfBuilder builder) { builder.add( - isReturnVoid() ? new CfReturnVoid() : new CfReturn(ValueType.fromType(getReturnType()))); + isReturnVoid() ? new CfReturnVoid() : new CfReturn(ValueType.fromType(getReturnType())), + this); } public static class Builder extends BuilderBase<Builder, Return> {
diff --git a/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java b/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java index 72a62cd..b811f86 100644 --- a/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java +++ b/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
@@ -22,7 +22,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfSafeCheckCast(getType())); + builder.add(new CfSafeCheckCast(getType()), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java index c7e076c..c4413d0 100644 --- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java +++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -214,7 +214,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfStaticFieldRead(getField(), builder.resolveField(getField()))); + builder.add(new CfStaticFieldRead(getField(), builder.resolveField(getField())), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java index 339f16f..2230cc7 100644 --- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java +++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -221,7 +221,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfStaticFieldWrite(getField(), builder.resolveField(getField()))); + builder.add(new CfStaticFieldWrite(getField(), builder.resolveField(getField())), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Store.java b/src/main/java/com/android/tools/r8/ir/code/Store.java index 8100111..6714e0d 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Store.java +++ b/src/main/java/com/android/tools/r8/ir/code/Store.java
@@ -77,7 +77,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfStore(outType(), builder.getLocalRegister(outValue))); + builder.add(new CfStore(outType(), builder.getLocalRegister(outValue)), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Swap.java b/src/main/java/com/android/tools/r8/ir/code/Swap.java index 777eec1..87ed900 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Swap.java +++ b/src/main/java/com/android/tools/r8/ir/code/Swap.java
@@ -67,7 +67,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfStackInstruction(Opcode.Swap)); + builder.add(new CfStackInstruction(Opcode.Swap), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java index 6f47788..6350255 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Throw.java +++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -83,7 +83,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add(new CfThrow()); + builder.add(new CfThrow(), this); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java index 34142c2..4f4a7b2 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -29,6 +29,8 @@ import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.Argument; import com.android.tools.r8.ir.code.BasicBlock; @@ -102,6 +104,9 @@ private List<InvokeDirect> thisInitializers; private Map<NewInstance, CfLabel> newInstanceLabels; + // Extra information that should be attached to the bytecode instructions. + private final BytecodeMetadata.Builder<CfInstruction> bytecodeMetadataBuilder; + // Internal structure maintaining the stack height. private static class StackHeightTracker { int maxHeight = 0; @@ -128,10 +133,15 @@ } } - public CfBuilder(AppView<?> appView, DexEncodedMethod method, IRCode code) { + public CfBuilder( + AppView<?> appView, + DexEncodedMethod method, + IRCode code, + BytecodeMetadataProvider bytecodeMetadataProvider) { this.appView = appView; this.method = method; this.code = code; + this.bytecodeMetadataBuilder = BytecodeMetadata.builder(bytecodeMetadataProvider); } public CfCode build(DeadCodeRemover deadCodeRemover, MethodConversionOptions conversionOptions) { @@ -383,7 +393,8 @@ instructions, tryCatchRanges, localVariablesTable, - diagnosticPosition); + diagnosticPosition, + bytecodeMetadataBuilder.build()); } private static boolean isNopInstruction(Instruction instruction, BasicBlock nextBlock) { @@ -684,11 +695,25 @@ return registerAllocator.getRegisterForValue(value); } - public void add(CfInstruction instruction) { + private void add(CfInstruction instruction) { instructions.add(instruction); } + public void add(CfInstruction instruction, Instruction origin) { + bytecodeMetadataBuilder.setMetadata(origin, instruction); + add(instruction); + } + + public void add(CfInstruction... instructions) { + Collections.addAll(this.instructions, instructions); + } + public void addArgument(Argument argument) { // Nothing so far. } + + public boolean verifyNoMetadata(Instruction instruction) { + assert bytecodeMetadataBuilder.verifyNoMetadata(instruction); + return true; + } }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java index c19a35b..efd8593 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1486,7 +1486,13 @@ printMethod(code, "Optimized IR (SSA)", previous); timing.begin("Finalize IR"); - finalizeIR(code, feedback, conversionOptions, bytecodeMetadataProviderBuilder.build(), timing); + finalizeIR( + context, + code, + feedback, + conversionOptions, + bytecodeMetadataProviderBuilder.build(), + timing); timing.end(); return timing; } @@ -1579,6 +1585,7 @@ } deadCodeRemover.run(code, timing); finalizeIR( + code.context(), code, feedback, DefaultMethodConversionOptions.getInstance(), @@ -1587,6 +1594,7 @@ } public void finalizeIR( + ProgramMethod method, IRCode code, OptimizationFeedback feedback, MethodConversionOptions conversionOptions, @@ -1594,7 +1602,7 @@ Timing timing) { code.traceBlocks(); if (options.isGeneratingClassFiles()) { - finalizeToCf(code, feedback, conversionOptions); + finalizeToCf(code, feedback, conversionOptions, bytecodeMetadataProvider); } else { assert options.isGeneratingDex(); finalizeToDex(code, feedback, conversionOptions, bytecodeMetadataProvider, timing); @@ -1602,10 +1610,13 @@ } private void finalizeToCf( - IRCode code, OptimizationFeedback feedback, MethodConversionOptions conversionOptions) { + IRCode code, + OptimizationFeedback feedback, + MethodConversionOptions conversionOptions, + BytecodeMetadataProvider bytecodeMetadataProvider) { DexEncodedMethod method = code.method(); assert !method.getCode().isDexCode(); - CfBuilder builder = new CfBuilder(appView, method, code); + CfBuilder builder = new CfBuilder(appView, method, code, bytecodeMetadataProvider); CfCode result = builder.build(deadCodeRemover, conversionOptions); method.setCode(result, appView); markProcessed(code, feedback);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java index fc60cb9..cd3a6b1 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
@@ -103,7 +103,8 @@ apiLevelCompute.computeApiLevelForLibraryReference( cfInvoke.getMethod(), ComputedApiLevel.unknown()); if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(methodApiLevel) - || isApiLevelLessThanOrEqualTo9(methodApiLevel)) { + || isApiLevelLessThanOrEqualTo9(methodApiLevel) + || methodApiLevel.isUnknownApiLevel()) { return appView.computedMinApiLevel(); } // Compute the api level of the holder to see if the method will be stubbed.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java index d30f717..6740441 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -693,9 +693,7 @@ DexClassAndMethod companionMethod = helper.ensureDefaultAsMethodOfCompanionClassStub(method); acceptCompanionMethod(method, companionMethod, eventConsumer); - return getInvokeStaticInstructions( - InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass( - amendedMethod, appView.dexItemFactory())); + return getInvokeStaticInstructions(companionMethod.getReference()); }) .build(); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java index 3e8b5cb..f712cb4 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -227,8 +227,9 @@ .resolveMethod(derivedMethod.getMethod(), true) .getResolvedProgramMethod(); caseMethod = - InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass( - resolvedProgramMethod.getReference(), appView.dexItemFactory()); + helper + .ensureDefaultAsMethodOfProgramCompanionClassStub(resolvedProgramMethod) + .getReference(); } extraDispatchCases.put(type, caseMethod); }); @@ -276,10 +277,12 @@ DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method.getReference()); if (result != null && !result.isAbstract()) { assert result.isDefaultMethod(); - extraDispatchCases.put( - subInterfaceClass.type, - InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass( - result.getReference(), appView.dexItemFactory())); + DexMethod forward = + helper + .ensureDefaultAsMethodOfProgramCompanionClassStub( + new ProgramMethod(subInterfaceClass.asProgramClass(), result)) + .getReference(); + extraDispatchCases.put(subInterfaceClass.type, forward); } } } else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java index 8e78a50..ba2c90f 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -5,7 +5,6 @@ package com.android.tools.r8.ir.optimize; import com.android.tools.r8.AssertionsConfiguration; -import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; @@ -32,7 +31,7 @@ private static class ConfigurationEntryWithDexString { - private AssertionsConfiguration entry; + private final AssertionsConfiguration entry; private final DexString value; private ConfigurationEntryWithDexString( @@ -72,13 +71,29 @@ throw new Unreachable(); } } + + public boolean isEnabled() { + return entry.isCompileTimeEnabled(); + } + + public boolean isDisabled() { + return entry.isCompileTimeDisabled(); + } + + public boolean isPassthrough() { + return entry.isPassthrough(); + } + + public boolean isAssertionHandler() { + return entry.isAssertionHandler(); + } } private final AppView<?> appView; private final DexItemFactory dexItemFactory; - private final AssertionTransformation defaultTransformation; + private final ConfigurationEntryWithDexString defaultConfiguration; private final List<ConfigurationEntryWithDexString> configuration; - private final AssertionsConfiguration.AssertionTransformation kotlinTransformation; + private final ConfigurationEntryWithDexString kotlinTransformation; private final boolean enabled; public AssertionsRewriter(AppView<?> appView) { @@ -86,13 +101,15 @@ this.dexItemFactory = appView.dexItemFactory(); this.enabled = isEnabled(appView.options()); if (!enabled) { - defaultTransformation = null; + defaultConfiguration = null; configuration = null; kotlinTransformation = null; return; } - // Convert the assertion transformation to the representation used for this rewriter. - this.defaultTransformation = appView.options().assertionsConfiguration.defautlTransformation; + // Convert the assertion configuration to the representation used for this rewriter. + this.defaultConfiguration = + new ConfigurationEntryWithDexString( + appView.options().assertionsConfiguration.defaultConfiguration, dexItemFactory); this.configuration = appView.options().assertionsConfiguration.assertionsConfigurations.stream() .map(entry -> new ConfigurationEntryWithDexString(entry, appView.dexItemFactory())) @@ -108,40 +125,40 @@ return configuration != null && !configuration.isPassthroughAll(); } - private AssertionTransformation getTransformationForMethod(DexEncodedMethod method) { + private ConfigurationEntryWithDexString getTransformationForMethod(DexEncodedMethod method) { return getTransformationForType(method.getHolderType()); } - private AssertionTransformation getTransformationForType(DexType type) { - AssertionTransformation transformation = defaultTransformation; + private ConfigurationEntryWithDexString getTransformationForType(DexType type) { + ConfigurationEntryWithDexString result = defaultConfiguration; for (ConfigurationEntryWithDexString entry : configuration) { switch (entry.entry.getScope()) { case ALL: - transformation = entry.entry.getTransformation(); + result = entry; break; case PACKAGE: if (entry.value.size == 0) { if (!type.descriptor.contains(dexItemFactory.descriptorSeparator)) { - transformation = entry.entry.getTransformation(); + result = entry; } } else if (type.descriptor.startsWith(entry.value)) { - transformation = entry.entry.getTransformation(); + result = entry; } break; case CLASS: if (type.descriptor.equals(entry.value)) { - transformation = entry.entry.getTransformation(); + result = entry; } if (isDescriptorForClassOrInnerClass(entry.value, type.descriptor)) { - transformation = entry.entry.getTransformation(); + result = entry; } break; default: throw new Unreachable(); } } - assert transformation != null; - return transformation; + assert result != null; + return result; } private boolean isDescriptorForClassOrInnerClass( @@ -310,8 +327,8 @@ } private void runInternal(DexEncodedMethod method, IRCode code) { - AssertionTransformation transformation = getTransformationForMethod(method); - if (transformation == AssertionTransformation.PASSTHROUGH) { + ConfigurationEntryWithDexString configuration = getTransformationForMethod(method); + if (configuration.isPassthrough()) { return; } DexEncodedMethod clinit; @@ -338,7 +355,7 @@ InvokeMethod invoke = current.asInvokeMethod(); if (invoke.getInvokedMethod() == dexItemFactory.classMethods.desiredAssertionStatus) { if (method.getHolderType() == dexItemFactory.kotlin.assertions.type) { - rewriteKotlinAssertionEnable(code, transformation, iterator, invoke); + rewriteKotlinAssertionEnable(code, configuration, iterator, invoke); } else { iterator.replaceCurrentInstruction(code.createIntConstant(0, current.getLocalInfo())); } @@ -355,16 +372,13 @@ if (isInitializerEnablingJavaVmAssertions && staticGet.getField().name == dexItemFactory.assertionsDisabled) { iterator.replaceCurrentInstruction( - code.createIntConstant( - transformation == AssertionTransformation.DISABLE ? 1 : 0, - current.getLocalInfo())); + code.createIntConstant(configuration.isDisabled() ? 1 : 0, current.getLocalInfo())); } // Rewrite kotlin._Assertions.ENABLED getter. if (staticGet.getField() == dexItemFactory.kotlin.assertions.enabledField) { iterator.replaceCurrentInstruction( code.createIntConstant( - kotlinTransformation == AssertionTransformation.DISABLE ? 0 : 1, - current.getLocalInfo())); + kotlinTransformation.isDisabled() ? 0 : 1, current.getLocalInfo())); } } } @@ -372,10 +386,10 @@ private void rewriteKotlinAssertionEnable( IRCode code, - AssertionTransformation transformation, + ConfigurationEntryWithDexString configuration, InstructionListIterator iterator, InvokeMethod invoke) { - if (iterator.hasNext() && transformation == AssertionTransformation.DISABLE) { + if (iterator.hasNext() && configuration.isDisabled()) { // Check if the invocation of Class.desiredAssertionStatus() is followed by a static // put to kotlin._Assertions.ENABLED, and if so remove both instructions. // See comment in ClassInitializerAssertionEnablingAnalysis for the expected instruction @@ -402,8 +416,7 @@ iterator.replaceCurrentInstruction(code.createIntConstant(0)); } } else { - iterator.replaceCurrentInstruction( - code.createIntConstant(transformation == AssertionTransformation.ENABLE ? 1 : 0)); + iterator.replaceCurrentInstruction(code.createIntConstant(configuration.isEnabled() ? 1 : 0)); } } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java index a39688a..8182229 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -130,8 +130,9 @@ WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { // Do not inline if the inlinee is greater than the api caller level. // TODO(b/188498051): We should not force inline lower api method calls. - if (reason != Reason.FORCE && !isApiSafeForInlining(method, singleTarget, appView.options())) { - whyAreYouNotInliningReporter.reportInlineeHigherApiCall(); + if (reason != Reason.FORCE + && !isApiSafeForInlining( + method, singleTarget, appView.options(), whyAreYouNotInliningReporter)) { return false; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java index 834e78d..a658752 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -577,8 +577,7 @@ && !isSynthesizingNullCheckForReceiverUsingMonitorEnter) { SimpleEffectAnalysisResult checksReceiverBeingNull = canInlineWithoutSynthesizingNullCheckForReceiver(appView, code); - if (!checksReceiverBeingNull.hasResult() - || !checksReceiverBeingNull.isPartial() + if (checksReceiverBeingNull.isNotSatisfied() || (checksReceiverBeingNull.isPartial() && checksReceiverBeingNull.topMostNotSatisfiedBlockSize() > 1)) { synthesizeNullCheckForReceiver(appView, code, invoke, code.entryBlock()); @@ -761,13 +760,11 @@ assert false : "Expected position for inlinee call to receiver"; return; } + Position outermostCaller = position.getOutermostCaller(); Position removeInnerFrame = - position - .getOutermostCaller() - .builderWithCopy() - .setRemoveInnerFramesIfThrowingNpe(true) - .build(); - instruction.forceOverwritePosition(position.replaceOutermostCallerPosition(removeInnerFrame)); + outermostCaller.builderWithCopy().setRemoveInnerFramesIfThrowingNpe(true).build(); + instruction.forceOverwritePosition( + position.replacePosition(outermostCaller, removeInnerFrame)); } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java index 50bea08..984b6c1 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -19,6 +19,8 @@ import com.android.tools.r8.ir.analysis.value.SingleFieldValue; import com.android.tools.r8.ir.analysis.value.SingleValue; import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState; +import com.android.tools.r8.ir.code.ArrayGet; +import com.android.tools.r8.ir.code.ArrayPut; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.FieldGet; import com.android.tools.r8.ir.code.FieldInstruction; @@ -30,6 +32,7 @@ import com.android.tools.r8.ir.code.InstructionListIterator; import com.android.tools.r8.ir.code.InvokeDirect; import com.android.tools.r8.ir.code.InvokeStatic; +import com.android.tools.r8.ir.code.MemberType; import com.android.tools.r8.ir.code.NewInstance; import com.android.tools.r8.ir.code.Phi; import com.android.tools.r8.ir.code.StaticGet; @@ -47,6 +50,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; /** @@ -88,7 +92,9 @@ public static boolean shouldRun(AppView<?> appView, IRCode code) { return appView.options().enableRedundantFieldLoadElimination - && (code.metadata().mayHaveFieldInstruction() || code.metadata().mayHaveInitClass()); + && (code.metadata().mayHaveArrayGet() + || code.metadata().mayHaveFieldInstruction() + || code.metadata().mayHaveInitClass()); } private interface FieldValue { @@ -97,7 +103,7 @@ return null; } - void eliminateRedundantRead(InstructionListIterator it, FieldInstruction redundant); + void eliminateRedundantRead(InstructionListIterator it, Instruction redundant); } private class ExistingValue implements FieldValue { @@ -114,9 +120,9 @@ } @Override - public void eliminateRedundantRead(InstructionListIterator it, FieldInstruction redundant) { - affectedValues.addAll(redundant.value().affectedValues()); - redundant.value().replaceUsers(value); + public void eliminateRedundantRead(InstructionListIterator it, Instruction redundant) { + affectedValues.addAll(redundant.outValue().affectedValues()); + redundant.outValue().replaceUsers(value); it.removeOrReplaceByDebugLocalRead(); value.uniquePhiUsers().forEach(Phi::removeTrivialPhi); } @@ -141,13 +147,106 @@ } @Override - public void eliminateRedundantRead(InstructionListIterator it, FieldInstruction redundant) { - affectedValues.addAll(redundant.value().affectedValues()); + public void eliminateRedundantRead(InstructionListIterator it, Instruction redundant) { + affectedValues.addAll(redundant.outValue().affectedValues()); it.replaceCurrentInstruction( value.createMaterializingInstruction(appView.withClassHierarchy(), code, redundant)); } } + private abstract static class ArraySlot { + + protected final Value array; + protected final MemberType memberType; + + private ArraySlot(Value array, MemberType memberType) { + this.array = array; + this.memberType = memberType; + } + + public static ArraySlot create(Value array, Value index, MemberType memberType) { + if (index.isDefinedByInstructionSatisfying(Instruction::isConstNumber)) { + return new ArraySlotWithConstantIndex( + array, index.getDefinition().asConstNumber().getIntValue(), memberType); + } + return new ArraySlotWithValueIndex(array, index, memberType); + } + + public MemberType getMemberType() { + return memberType; + } + + public abstract boolean maybeHasIndex(int i); + + boolean baseEquals(ArraySlot arraySlot) { + return array == arraySlot.array && memberType == arraySlot.memberType; + } + } + + private static class ArraySlotWithConstantIndex extends ArraySlot { + + private final int index; + + private ArraySlotWithConstantIndex(Value array, int index, MemberType memberType) { + super(array, memberType); + this.index = index; + } + + @Override + public boolean maybeHasIndex(int i) { + return index == i; + } + + @Override + public int hashCode() { + return Objects.hash(array, index, memberType); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + ArraySlotWithConstantIndex arraySlot = (ArraySlotWithConstantIndex) other; + return index == arraySlot.index && baseEquals(arraySlot); + } + } + + private static class ArraySlotWithValueIndex extends ArraySlot { + + private final Value index; + + private ArraySlotWithValueIndex(Value array, Value index, MemberType memberType) { + super(array, memberType); + this.index = index; + } + + @Override + public boolean maybeHasIndex(int i) { + return true; + } + + @Override + public int hashCode() { + return Objects.hash(array, index, memberType); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + ArraySlotWithValueIndex arraySlot = (ArraySlotWithValueIndex) other; + return index == arraySlot.index && baseEquals(arraySlot); + } + } + private static class FieldAndObject { private final DexField field; private final Value object; @@ -217,7 +316,14 @@ InstructionListIterator it = block.listIterator(code); while (it.hasNext()) { Instruction instruction = it.next(); - if (instruction.isFieldInstruction()) { + if (instruction.isArrayAccess()) { + if (instruction.isArrayGet()) { + handleArrayGet(it, instruction.asArrayGet()); + } else { + assert instruction.isArrayPut(); + handleArrayPut(instruction.asArrayPut()); + } + } else if (instruction.isFieldInstruction()) { DexField reference = instruction.asFieldInstruction().getField(); DexClassAndField field = resolveField(reference); if (field == null || field.getDefinition().isVolatile()) { @@ -446,6 +552,43 @@ } } + private void handleArrayGet(InstructionListIterator it, ArrayGet arrayGet) { + if (arrayGet.outValue().hasLocalInfo()) { + return; + } + + Value array = arrayGet.array().getAliasedValue(); + Value index = arrayGet.index().getAliasedValue(); + ArraySlot arraySlot = ArraySlot.create(array, index, arrayGet.getMemberType()); + FieldValue replacement = activeState.getArraySlotValue(arraySlot); + if (replacement != null) { + replacement.eliminateRedundantRead(it, arrayGet); + return; + } + + activeState.putArraySlotValue(arraySlot, new ExistingValue(arrayGet.outValue())); + } + + private void handleArrayPut(ArrayPut arrayPut) { + int index = arrayPut.getIndexOrDefault(-1); + MemberType memberType = arrayPut.getMemberType(); + + // An array-put instruction can potentially write the given array slot on all arrays because of + // aliases. + if (index < 0) { + activeState.removeArraySlotValues(memberType); + } else { + activeState.removeArraySlotValues(memberType, index); + } + + // Update the value of the field to allow redundant load elimination. + Value array = arrayPut.array().getAliasedValue(); + Value indexValue = arrayPut.index().getAliasedValue(); + ArraySlot arraySlot = ArraySlot.create(array, indexValue, memberType); + ExistingValue value = new ExistingValue(arrayPut.value()); + activeState.putArraySlotValue(arraySlot, value); + } + private void handleInstanceGet( InstructionListIterator it, InstanceGet instanceGet, @@ -646,6 +789,7 @@ } private void killAllNonFinalActiveFields() { + activeState.clearArraySlotValues(); activeState.clearNonFinalInstanceFields(); activeState.clearNonFinalStaticFields(); activeState.clearMostRecentFieldWrites(); @@ -801,6 +945,8 @@ static class BlockState { + private LinkedHashMap<ArraySlot, FieldValue> arraySlotValues; + private LinkedHashMap<FieldAndObject, FieldValue> finalInstanceFieldValues; private LinkedHashMap<DexField, FieldValue> finalStaticFieldValues; @@ -826,6 +972,10 @@ public BlockState(int maxCapacity, BlockState state) { this(maxCapacity); if (state != null) { + if (state.arraySlotValues != null && !state.arraySlotValues.isEmpty()) { + arraySlotValues = new LinkedHashMap<>(); + arraySlotValues.putAll(state.arraySlotValues); + } if (state.finalInstanceFieldValues != null && !state.finalInstanceFieldValues.isEmpty()) { finalInstanceFieldValues = new LinkedHashMap<>(); finalInstanceFieldValues.putAll(state.finalInstanceFieldValues); @@ -861,6 +1011,10 @@ } } + public void clearArraySlotValues() { + arraySlotValues = null; + } + public void clearMostRecentFieldWrites() { clearMostRecentInstanceFieldWrites(); clearMostRecentStaticFieldWrites(); @@ -902,6 +1056,10 @@ } } + public FieldValue getArraySlotValue(ArraySlot arraySlot) { + return arraySlotValues != null ? arraySlotValues.get(arraySlot) : null; + } + public FieldValue getInstanceFieldValue(FieldAndObject field) { FieldValue value = nonFinalInstanceFieldValues != null ? nonFinalInstanceFieldValues.get(field) : null; @@ -921,6 +1079,11 @@ } public void intersect(BlockState state) { + if (arraySlotValues != null && state.arraySlotValues != null) { + intersectFieldValues(arraySlotValues, state.arraySlotValues); + } else { + arraySlotValues = null; + } if (finalInstanceFieldValues != null && state.finalInstanceFieldValues != null) { intersectFieldValues(finalInstanceFieldValues, state.finalInstanceFieldValues); } else { @@ -962,7 +1125,9 @@ } public boolean isEmpty() { - return isEmpty(finalInstanceFieldValues) + return isEmpty(arraySlotValues) + && isEmpty(initializedClasses) + && isEmpty(finalInstanceFieldValues) && isEmpty(finalStaticFieldValues) && isEmpty(initializedClasses) && isEmpty(nonFinalInstanceFieldValues) @@ -1008,6 +1173,7 @@ public void reduceSize(int numberOfItemsToRemove) { assert numberOfItemsToRemove > 0; assert numberOfItemsToRemove < size(); + numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, arraySlotValues); numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, initializedClasses); numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, nonFinalInstanceFieldValues); numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, nonFinalStaticFieldValues); @@ -1035,6 +1201,22 @@ return reduceSize(numberOfItemsToRemove, map != null ? map.keySet() : null); } + public void removeArraySlotValues(MemberType memberType) { + if (arraySlotValues != null) { + arraySlotValues.keySet().removeIf(arraySlot -> arraySlot.getMemberType() == memberType); + } + } + + public void removeArraySlotValues(MemberType memberType, int index) { + if (arraySlotValues != null) { + arraySlotValues + .keySet() + .removeIf( + arraySlot -> + arraySlot.getMemberType() == memberType && arraySlot.maybeHasIndex(index)); + } + } + public void removeInstanceField(FieldAndObject field) { removeFinalInstanceField(field); removeNonFinalInstanceField(field); @@ -1089,6 +1271,14 @@ } } + public void putArraySlotValue(ArraySlot arraySlot, FieldValue value) { + ensureCapacityForNewElement(); + if (arraySlotValues == null) { + arraySlotValues = new LinkedHashMap<>(); + } + arraySlotValues.put(arraySlot, value); + } + public void putFinalInstanceField(FieldAndObject field, FieldValue value) { ensureCapacityForNewElement(); if (finalInstanceFieldValues == null) { @@ -1155,7 +1345,8 @@ } public int size() { - return size(finalInstanceFieldValues) + return size(arraySlotValues) + + size(finalInstanceFieldValues) + size(finalStaticFieldValues) + size(initializedClasses) + size(nonFinalInstanceFieldValues)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java index 8942395..ae95f9e 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
@@ -166,18 +166,19 @@ public static class SimpleEffectAnalysisResult { + private final ResultState result; private final List<Instruction> satisfyingInstructions; private final List<BasicBlock> topmostNotSatisfiedBlocks; private SimpleEffectAnalysisResult( - List<Instruction> satisfyingInstructions, List<BasicBlock> topmostNotSatisfiedBlocks) { - + ResultState result, + List<Instruction> satisfyingInstructions, + List<BasicBlock> topmostNotSatisfiedBlocks) { + this.result = result; this.satisfyingInstructions = satisfyingInstructions; this.topmostNotSatisfiedBlocks = topmostNotSatisfiedBlocks; - } - - public boolean hasResult() { - return true; + assert !result.isPartial() + || (!satisfyingInstructions.isEmpty() && !topmostNotSatisfiedBlocks.isEmpty()); } public void forEachSatisfyingInstruction(Consumer<Instruction> instructionConsumer) { @@ -196,8 +197,12 @@ return new SimpleEffectAnalysisResultBuilder(); } + public boolean isNotSatisfied() { + return result.isNotSatisfied(); + } + public boolean isPartial() { - return !satisfyingInstructions.isEmpty() && !topmostNotSatisfiedBlocks.isEmpty(); + return result.isPartial(); } } @@ -205,10 +210,10 @@ List<Instruction> satisfyingInstructions = new ArrayList<>(); List<BasicBlock> failingBlocksForPartialResults = ImmutableList.of(); - private boolean isFailed; + private ResultState result; public void fail() { - isFailed = true; + result = ResultState.NOT_SATISFIED; } public void addSatisfyingInstruction(Instruction instruction) { @@ -219,20 +224,21 @@ this.failingBlocksForPartialResults = basicBlocks; } + public void setResult(ResultState result) { + this.result = result; + } + public SimpleEffectAnalysisResult build() { - return isFailed + return result.isNotComputed() ? NO_RESULT - : new SimpleEffectAnalysisResult(satisfyingInstructions, failingBlocksForPartialResults); + : new SimpleEffectAnalysisResult( + result, satisfyingInstructions, failingBlocksForPartialResults); } } private static final SimpleEffectAnalysisResult NO_RESULT = - new SimpleEffectAnalysisResult(ImmutableList.of(), ImmutableList.of()) { - @Override - public boolean hasResult() { - return false; - } - }; + new SimpleEffectAnalysisResult( + ResultState.NOT_SATISFIED, ImmutableList.of(), ImmutableList.of()); public static SimpleEffectAnalysisResult run(IRCode code, InstructionAnalysis analysis) { SimpleEffectAnalysisResultBuilder builder = SimpleEffectAnalysisResult.builder(); @@ -288,6 +294,7 @@ } node.setState(resultState); if (node.getNode().isEntry()) { + builder.setResult(resultState.state); builder.setFailingBlocksForPartialResults(resultState.failingBlocks); } return CONTINUE;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java index 5e3b23d..8a3d24e 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -167,23 +167,27 @@ offsetDiff = 1; builder.addArgumentInfo( 0, - new RewrittenPrototypeDescription.RewrittenTypeInfo( - from.holder, to.proto.parameters.values[0])); + RewrittenTypeInfo.builder() + .setOldType(from.getHolderType()) + .setNewType(to.getParameter(0)) + .build()); } - for (int i = 0; i < from.proto.parameters.size(); i++) { - DexType fromType = from.proto.parameters.values[i]; - DexType toType = to.proto.parameters.values[i + offsetDiff]; + for (int i = 0; i < from.getParameters().size(); i++) { + DexType fromType = from.getParameter(i); + DexType toType = to.getParameter(i + offsetDiff); if (fromType != toType) { builder.addArgumentInfo( i + offsetDiff + toOffset, - new RewrittenPrototypeDescription.RewrittenTypeInfo(fromType, toType)); + RewrittenTypeInfo.builder().setOldType(fromType).setNewType(toType).build()); } } - RewrittenPrototypeDescription.RewrittenTypeInfo returnInfo = - from.proto.returnType == to.proto.returnType + RewrittenTypeInfo returnInfo = + from.getReturnType() == to.getReturnType() ? null - : new RewrittenPrototypeDescription.RewrittenTypeInfo( - from.proto.returnType, to.proto.returnType); + : RewrittenTypeInfo.builder() + .setOldType(from.getReturnType()) + .setNewType(to.getReturnType()) + .build(); RewrittenPrototypeDescription prototypeChanges = RewrittenPrototypeDescription.createForRewrittenTypes(returnInfo, builder.build()) .withExtraUnusedNullParameters(numberOfExtraNullParameters);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java index cc7d4ec..d3d3735 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.optimize.inliner; +import com.android.tools.r8.androidapi.ComputedApiLevel; import com.android.tools.r8.ir.code.InstancePut; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InvokeDirect; @@ -34,6 +35,9 @@ public void reportCallerNotSubtype() {} @Override + public void reportCallerHasUnknownApiLevel() {} + + @Override public void reportClasspathMethod() {} @Override @@ -55,7 +59,8 @@ public void reportInlineeNotSimple() {} @Override - public void reportInlineeHigherApiCall() {} + public void reportInlineeHigherApiCall( + ComputedApiLevel callerApiLevel, ComputedApiLevel inlineeApiLevel) {} @Override public void reportInlineeRefersToClassesNotInMainDex() {}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java index 66d6108..7c3e5da 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.optimize.inliner; +import com.android.tools.r8.androidapi.ComputedApiLevel; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.InstancePut; @@ -53,6 +54,8 @@ public abstract void reportCallerNotSubtype(); + public abstract void reportCallerHasUnknownApiLevel(); + public abstract void reportClasspathMethod(); public abstract void reportInaccessible(); @@ -67,7 +70,8 @@ public abstract void reportInlineeNotSimple(); - public abstract void reportInlineeHigherApiCall(); + public abstract void reportInlineeHigherApiCall( + ComputedApiLevel callerApiLevel, ComputedApiLevel inlineeApiLevel); public abstract void reportInlineeRefersToClassesNotInMainDex();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java index 94632b6..f5807dd 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.optimize.inliner; +import com.android.tools.r8.androidapi.ComputedApiLevel; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.InstancePut; import com.android.tools.r8.ir.code.Instruction; @@ -73,6 +74,11 @@ } @Override + public void reportCallerHasUnknownApiLevel() { + print("computed API level for caller is unknown"); + } + + @Override public void reportClasspathMethod() { print("inlinee is on the classpath."); } @@ -115,8 +121,20 @@ } @Override - public void reportInlineeHigherApiCall() { - print("inlinee having a higher api call than caller context."); + public void reportInlineeHigherApiCall( + ComputedApiLevel callerApiLevel, ComputedApiLevel inlineeApiLevel) { + assert callerApiLevel.isKnownApiLevel(); + if (inlineeApiLevel.isUnknownApiLevel()) { + print("computed API level for inlinee is unknown"); + } else { + assert inlineeApiLevel.isKnownApiLevel(); + print( + "computed API level for inlinee (" + + inlineeApiLevel.asKnownApiLevel().getApiLevel() + + ") is higher than caller's (" + + callerApiLevel.asKnownApiLevel().getApiLevel() + + ")"); + } } @Override
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java index 70e6d04..339f93d 100644 --- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -174,7 +174,11 @@ clazz, (superType, superClass, isInterface) -> { if (isInterface && superClass.isNotProgramClass()) { - frontierStatesForInterfaces.get(superType).add(reservationState); + Set<ReservedFieldNamingState> reservedNamingState = + frontierStatesForInterfaces.get(superType); + if (reservedNamingState != null) { + reservedNamingState.add(reservationState); + } } return TraversalContinuation.CONTINUE; });
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java index 8067817..14f7711 100644 --- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.SubtypingInfo; import com.android.tools.r8.ir.desugar.LambdaDescriptor; import com.android.tools.r8.naming.MethodNameMinifier.State; import com.android.tools.r8.shaking.AppInfoWithLiveness; @@ -360,6 +361,7 @@ } private final AppView<AppInfoWithLiveness> appView; + private final SubtypingInfo subtypingInfo; private final Equivalence<DexMethod> equivalence; private final Equivalence<DexEncodedMethod> definitionEquivalence; private final MethodNameMinifier.State minifierState; @@ -371,9 +373,11 @@ /** A map for caching all interface states. */ private final Map<DexType, InterfaceReservationState> interfaceStateMap = new HashMap<>(); - InterfaceMethodNameMinifier(AppView<AppInfoWithLiveness> appView, State minifierState) { + InterfaceMethodNameMinifier( + AppView<AppInfoWithLiveness> appView, State minifierState, SubtypingInfo subtypingInfo) { this.appView = appView; this.minifierState = minifierState; + this.subtypingInfo = subtypingInfo; this.equivalence = appView.options().getProguardConfiguration().isOverloadAggressively() ? MethodSignatureEquivalence.get() @@ -420,7 +424,7 @@ // frontier states of classes that implement them. We add the frontier states so that we can // reserve the names for later method naming. timing.begin("Compute map"); - computeReservationFrontiersForAllImplementingClasses(); + computeReservationFrontiersForAllImplementingClasses(interfaces); for (DexClass iface : interfaces) { InterfaceReservationState inheritanceState = interfaceStateMap.get(iface.type); assert inheritanceState != null; @@ -454,9 +458,6 @@ // refer to interfaces which has been removed. Set<DexEncodedMethod> implementedMethods = appView.appInfo().lookupLambdaImplementedMethods(callSite); - if (implementedMethods.isEmpty()) { - return; - } for (DexEncodedMethod method : implementedMethods) { Wrapper<DexEncodedMethod> wrapped = definitionEquivalence.wrap(method); InterfaceMethodGroupState groupState = globalStateMap.get(wrapped); @@ -636,34 +637,29 @@ } } - private void computeReservationFrontiersForAllImplementingClasses() { - appView - .appInfo() - .forEachTypeInHierarchyOfLiveProgramClasses( - clazz -> { - // TODO(b/133091438): Extend the if check to test for !clazz.isLibrary(). - if (!clazz.isInterface()) { - appView - .appInfo() - .implementedInterfaces(clazz.type) - .forEach( - (directlyImplemented, ignoreIsKnownAndReserveInAllCases) -> { - InterfaceReservationState iState = - interfaceStateMap.get(directlyImplemented); - if (iState != null) { - DexType frontierType = minifierState.getFrontier(clazz.type); - iState.addReservationType(frontierType); - // The reservation state should already be added, but if a class is - // extending an interface, we will not visit the class during the - // sub-type traversel. - if (minifierState.getReservationState(clazz.type) == null) { - minifierState.allocateReservationStateAndReserve( - clazz.type, frontierType); - } - } - }); - } - }); + private void computeReservationFrontiersForAllImplementingClasses(Iterable<DexClass> interfaces) { + interfaces.forEach( + iface -> + subtypingInfo + .subtypes(iface.getType()) + .forEach( + subType -> { + DexClass subClass = appView.contextIndependentDefinitionFor(subType); + if (subClass == null || subClass.isInterface()) { + return; + } + DexType frontierType = minifierState.getFrontier(subType); + if (minifierState.getReservationState(frontierType) == null) { + // The reservation state should already be added. If it does not exist + // it is because it is not reachable from the type hierarchy of program + // classes and we can therefore disregard this interface. + return; + } + InterfaceReservationState iState = interfaceStateMap.get(iface.getType()); + if (iState != null) { + iState.addReservationType(frontierType); + } + })); } private boolean verifyAllCallSitesAreRepresentedIn(List<Wrapper<DexEncodedMethod>> groups) {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java index b2b9e9f..ca7fa4e 100644 --- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.MethodAccessInfoCollection; import com.android.tools.r8.graph.MethodResolutionResult; +import com.android.tools.r8.graph.SubtypingInfo; import com.android.tools.r8.graph.TopDownClassHierarchyTraversal; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.InternalOptions; @@ -181,7 +182,10 @@ } MethodRenaming computeRenaming( - Iterable<DexClass> interfaces, ExecutorService executorService, Timing timing) + Iterable<DexClass> interfaces, + SubtypingInfo subtypingInfo, + ExecutorService executorService, + Timing timing) throws ExecutionException { // Phase 1: Reserve all the names that need to be kept and allocate linked state in the // library part. @@ -193,7 +197,7 @@ // states that may hold an implementation. timing.begin("Phase 2"); InterfaceMethodNameMinifier interfaceMethodNameMinifier = - new InterfaceMethodNameMinifier(appView, minifierState); + new InterfaceMethodNameMinifier(appView, minifierState, subtypingInfo); timing.end(); timing.begin("Phase 3"); interfaceMethodNameMinifier.assignNamesToInterfaceMethods(timing, interfaces);
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java index 738be08..26c8d70 100644 --- a/src/main/java/com/android/tools/r8/naming/Minifier.java +++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -47,7 +47,7 @@ assert appView.options().isMinifying(); SubtypingInfo subtypingInfo = appView.appInfo().computeSubtypingInfo(); timing.begin("ComputeInterfaces"); - List<DexClass> interfaces = computeReachableInterfacesWithDeterministicOrder(); + List<DexClass> interfaces = computeReachableInterfacesWithDeterministicOrder(subtypingInfo); timing.end(); timing.begin("MinifyClasses"); ClassNameMinifier classNameMinifier = @@ -67,7 +67,7 @@ timing.begin("MinifyMethods"); MethodRenaming methodRenaming = new MethodNameMinifier(appView, minifyMembers) - .computeRenaming(interfaces, executorService, timing); + .computeRenaming(interfaces, subtypingInfo, executorService, timing); timing.end(); assert new MinifiedRenaming(appView, classRenaming, methodRenaming, FieldRenaming.empty()) @@ -89,9 +89,16 @@ return lens; } - private List<DexClass> computeReachableInterfacesWithDeterministicOrder() { + private List<DexClass> computeReachableInterfacesWithDeterministicOrder( + SubtypingInfo subtypingInfo) { List<DexClass> interfaces = new ArrayList<>(); - appView.appInfo().forEachReachableInterface(interfaces::add); + subtypingInfo.forAllInterfaceRoots( + type -> { + DexClass clazz = appView.contextIndependentDefinitionFor(type); + if (clazz != null) { + interfaces.add(clazz); + } + }); return classesWithDeterministicOrder(interfaces); }
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java index 7ee0681..43cf17d 100644 --- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -8,7 +8,6 @@ import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass; import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getInterfaceClassType; import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.isCompanionClassType; -import static com.android.tools.r8.utils.IterableUtils.fromMethod; import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; @@ -35,6 +34,7 @@ import com.android.tools.r8.position.Position; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.Reporter; +import com.android.tools.r8.utils.SetUtils; import com.android.tools.r8.utils.Timing; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; @@ -93,24 +93,34 @@ } public NamingLens run(ExecutorService executorService, Timing timing) throws ExecutionException { - AppInfoWithLiveness appInfo = appView.appInfo(); - SubtypingInfo subtypingInfo = appInfo.computeSubtypingInfo(); - ArrayDeque<Map<DexReference, MemberNaming>> nonPrivateMembers = new ArrayDeque<>(); Set<DexReference> notMappedReferences = new HashSet<>(); - + timing.begin("MappingInterfaces"); + Set<DexClass> classesToBuildSubtypeInformationFor = + SetUtils.newIdentityHashSet(appView.app().classes()); + appView + .appInfo() + .getObjectAllocationInfoCollection() + .forEachInstantiatedLambdaInterfaces( + type -> { + DexClass lambdaInterface = appView.contextIndependentDefinitionFor(type); + if (lambdaInterface != null) { + classesToBuildSubtypeInformationFor.add(lambdaInterface); + } + }); + appView.appInfo().forEachReferencedClasspathClass(classesToBuildSubtypeInformationFor::add); + SubtypingInfo subtypingInfo = + SubtypingInfo.create(classesToBuildSubtypeInformationFor, appView); timing.begin("MappingInterfaces"); Set<DexClass> interfaces = new TreeSet<>(Comparator.comparing(DexClass::getType)); - // For union-find of interface methods we also need to add the library types above live types. - appInfo.forEachReachableInterface( + subtypingInfo.forAllInterfaceRoots( iFace -> { - assert iFace.isInterface(); - interfaces.add(iFace); - if (iFace.interfaces.isEmpty()) { - computeMapping(iFace.type, nonPrivateMembers, notMappedReferences, subtypingInfo); + DexClass iFaceDefinition = appView.definitionFor(iFace); + if (iFaceDefinition != null) { + interfaces.add(iFaceDefinition); + computeMapping(iFace, nonPrivateMembers, notMappedReferences, subtypingInfo); } - }, - fromMethod(appInfo::forEachReferencedClasspathClass, DexClass::getType)); + }); assert nonPrivateMembers.isEmpty(); timing.end(); @@ -148,7 +158,7 @@ timing.begin("MinifyMethods"); MethodRenaming methodRenaming = new MethodNameMinifier(appView, nameStrategy) - .computeRenaming(interfaces, executorService, timing); + .computeRenaming(interfaces, subtypingInfo, executorService, timing); // Amend the method renamings with the default interface methods. methodRenaming.renaming.putAll(defaultInterfaceMethodImplementationNames); methodRenaming.renaming.putAll(additionalMethodNamings);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java index aa91add..63c2dfc 100644 --- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java +++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -64,10 +64,9 @@ private ProguardMapSupplier(ClassNameMapper classNameMapper, InternalOptions options) { assert classNameMapper != null; this.classNameMapper = classNameMapper.sorted(); - this.consumer = - InternalOptions.assertionsEnabled() - ? new ProguardMapChecker(options.proguardMapConsumer) - : options.proguardMapConsumer; + // TODO(b/217111432): Validate Proguard using ProguardMapChecker without building the entire + // Proguard map in memory. + this.consumer = options.proguardMapConsumer; this.options = options; this.reporter = options.reporter; }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java index 96b68f3..f5b55c6 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -225,7 +225,7 @@ timing.end(); timing.begin("Compute unused arguments"); - effectivelyUnusedArgumentsAnalysis.computeEffectivelyUnusedArguments(codeScannerResult); + effectivelyUnusedArgumentsAnalysis.computeEffectivelyUnusedArguments(); effectivelyUnusedArgumentsAnalysis = null; timing.end(); }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java index 3417bfa..c428a70 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
@@ -130,9 +130,30 @@ .getPosition() .getOutermostCallerMatchingOrElse( Position::isRemoveInnerFramesIfThrowingNpe, invoke.getPosition()); + if (nullCheckPosition.isRemoveInnerFramesIfThrowingNpe()) { + // We've found an outermost removeInnerFrames for an invoke with receiver. Assume we + // have call chain: inline -> callerInline -> callerCallerInline + // where callerInline.isRemoveInnerFramesIfThrowingNpe() == true. + // inline must therefore have an immediate use of the receiver which is tracked in + // callerInline such that the frame can be removed when retracing. + // When synthesizing a nullCheck for the receiver on inline, the check should actually + // fail in the callerInline. We therefore use this as the new position. + // Since the exception is now moved out to callerInline, we should no longer strip the + // topmost frame if we see an NPE in inline, so we update the position on this invoke to + // inline -> callerInline' -> callerCallerInline + // where callerInline.isRemoveInnerFramesIfThrowingNpe() == false; + Position newCallerPositionTail = + nullCheckPosition + .builderWithCopy() + .setRemoveInnerFramesIfThrowingNpe(false) + .build(); + invoke.forceOverwritePosition( + invoke.getPosition().replacePosition(nullCheckPosition, newCallerPositionTail)); + // We can then use pos2 (newCallerPositionTail) as the new null-check position. + nullCheckPosition = newCallerPositionTail; + } instructionIterator.insertNullCheckInstruction( appView, code, blockIterator, receiver, nullCheckPosition); - // Reset the block iterator. if (invoke.getBlock().hasCatchHandlers()) { BasicBlock splitBlock = invoke.getBlock();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java index c1a322b..b0be28f 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
@@ -16,7 +16,6 @@ import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter; -import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference; import com.android.tools.r8.optimize.argumentpropagation.utils.ParameterRemovalUtils; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.ListUtils; @@ -172,11 +171,11 @@ return effectivelyUnusedConstraints; } - public void computeEffectivelyUnusedArguments(MethodStateCollectionByReference methodStates) { + public void computeEffectivelyUnusedArguments() { // Build a graph where nodes are method parameters and there is an edge from method parameter p0 // to method parameter p1 if the removal of p0 depends on the removal of p1. EffectivelyUnusedArgumentsGraph dependenceGraph = - EffectivelyUnusedArgumentsGraph.create(appView, constraints, methodStates); + EffectivelyUnusedArgumentsGraph.create(appView, constraints); // Remove all unoptimizable method parameters from the graph, as well as all nodes that depend // on a node that is unoptimable.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java index fdc01bc..9fbd408 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java
@@ -10,7 +10,6 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo; import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter; -import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.WorkList; import com.android.tools.r8.utils.dfs.DFSStack; @@ -38,14 +37,13 @@ public static EffectivelyUnusedArgumentsGraph create( AppView<AppInfoWithLiveness> appView, - Map<MethodParameter, Set<MethodParameter>> constraints, - MethodStateCollectionByReference methodStates) { + Map<MethodParameter, Set<MethodParameter>> constraints) { EffectivelyUnusedArgumentsGraph graph = new EffectivelyUnusedArgumentsGraph(appView); constraints.forEach( (methodParameter, constraintsForMethodParameter) -> { EffectivelyUnusedArgumentsGraphNode node = graph.getOrCreateNode(methodParameter); for (MethodParameter constraint : constraintsForMethodParameter) { - graph.addConstraintEdge(node, constraint, constraints, methodStates); + graph.addConstraintEdge(node, constraint, constraints); } }); return graph; @@ -54,8 +52,7 @@ void addConstraintEdge( EffectivelyUnusedArgumentsGraphNode node, MethodParameter constraint, - Map<MethodParameter, Set<MethodParameter>> constraints, - MethodStateCollectionByReference methodStates) { + Map<MethodParameter, Set<MethodParameter>> constraints) { ProgramMethod dependencyMethod = asProgramMethodOrNull(appView.definitionFor(constraint.getMethod())); if (dependencyMethod == null) { @@ -68,7 +65,7 @@ // invoke (or we cannot preserve NPE semantics). if (dependencyMethod.getDefinition().isInstance() && constraint.getIndex() == 0 - && node.isNullable(methodStates)) { + && node.isNullable()) { node.setUnoptimizable(); return; }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java index 2b52686..c093924 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java
@@ -7,10 +7,7 @@ import static com.android.tools.r8.ir.optimize.info.OptimizationFeedback.getSimpleFeedback; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState; -import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState; -import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference; -import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState; +import com.android.tools.r8.ir.analysis.type.DynamicType; import com.google.common.collect.Sets; import java.util.BitSet; import java.util.Set; @@ -75,28 +72,13 @@ return successors; } - boolean isNullable(MethodStateCollectionByReference methodStates) { + boolean isNullable() { if (method.getDefinition().isInstance() && argumentIndex == 0) { return false; } - MethodState methodState = methodStates.get(method); - if (methodState.isBottom()) { - // TODO: this means the method is unreachable? what to do in this case? - return true; - } - assert !methodState.isBottom(); - if (methodState.isUnknown()) { - return true; - } - assert methodState.isMonomorphic(); - ConcreteMonomorphicMethodState monomorphicMethodState = methodState.asMonomorphic(); - ParameterState parameterState = monomorphicMethodState.getParameterState(argumentIndex); - if (parameterState.isUnknown()) { - return true; - } - assert parameterState.isConcrete(); - assert parameterState.asConcrete().isReferenceParameter(); - return parameterState.asConcrete().asReferenceParameter().getNullability().isMaybeNull(); + DynamicType dynamicType = + method.getOptimizationInfo().getArgumentInfos().getDynamicType(argumentIndex); + return dynamicType.getNullability().isNullable(); } boolean isUnoptimizable() {
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java index 14b393c..b4467ea 100644 --- a/src/main/java/com/android/tools/r8/retrace/Retrace.java +++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -602,7 +602,10 @@ RetraceStackTraceElementProxy<T, ST> other, Function<RetraceStackTraceElementProxy<T, ST>, Boolean> predicate, Function<RetraceStackTraceElementProxy<T, ST>, V> getter) { - return Comparator.comparing(predicate).thenComparing(getter).compare(one, other) != 0; + return Comparator.comparing(predicate) + .thenComparing(getter, Comparator.nullsFirst(V::compareTo)) + .compare(one, other) + != 0; } public static <T, ST extends StackTraceElementProxy<T, ST>>
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java index 94e2d31..f29af88 100644 --- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java +++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
@@ -10,7 +10,6 @@ import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.RewriteAction; import com.android.tools.r8.references.ClassReference; import com.android.tools.r8.retrace.RetraceStackTraceContext; -import com.android.tools.r8.utils.ListUtils; import java.util.List; import java.util.OptionalInt; @@ -36,12 +35,13 @@ } RetraceStackTraceCurrentEvaluationInformation.Builder builder = RetraceStackTraceCurrentEvaluationInformation.builder(); - MappedRange last = ListUtils.last(mappedRanges); - for (RewriteFrameMappingInformation rewriteInformation : - last.getRewriteFrameMappingInformation()) { - if (evaluateConditions(rewriteInformation.getConditions())) { - for (RewriteAction action : rewriteInformation.getActions()) { - action.evaluate(builder); + for (MappedRange mappedRange : mappedRanges) { + for (RewriteFrameMappingInformation rewriteInformation : + mappedRange.getRewriteFrameMappingInformation()) { + if (evaluateConditions(rewriteInformation.getConditions())) { + for (RewriteAction action : rewriteInformation.getActions()) { + action.evaluate(builder); + } } } }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java index 71d4807..78c8797 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1540,57 +1540,6 @@ app().asDirect().classpathClasses().forEach(fn); } - /** - * Visits all class definitions that are a live program type or a type above it in the hierarchy. - * - * <p>Any given definition will be visited at most once. No guarantees are places on the order. - */ - public void forEachTypeInHierarchyOfLiveProgramClasses(Consumer<DexClass> fn) { - forEachTypeInHierarchyOfLiveProgramClasses( - fn, - ListUtils.map(liveTypes, t -> definitionFor(t).asProgramClass()), - objectAllocationInfoCollection.getInstantiatedLambdaInterfaces(), - this); - } - - // Split in a static method so it can be used during construction. - static void forEachTypeInHierarchyOfLiveProgramClasses( - Consumer<DexClass> fn, - Collection<DexProgramClass> liveProgramClasses, - Set<DexType> lambdaInterfaces, - AppInfoWithClassHierarchy appInfo) { - Set<DexType> seen = Sets.newIdentityHashSet(); - liveProgramClasses.forEach(c -> seen.add(c.type)); - Deque<DexType> worklist = new ArrayDeque<>(lambdaInterfaces); - for (DexProgramClass liveProgramClass : liveProgramClasses) { - fn.accept(liveProgramClass); - DexType superType = liveProgramClass.superType; - if (superType != null && seen.add(superType)) { - worklist.add(superType); - } - for (DexType iface : liveProgramClass.interfaces.values) { - if (seen.add(iface)) { - worklist.add(iface); - } - } - } - while (!worklist.isEmpty()) { - DexType type = worklist.pop(); - DexClass clazz = appInfo.definitionFor(type); - if (clazz != null) { - fn.accept(clazz); - if (clazz.superType != null && seen.add(clazz.superType)) { - worklist.add(clazz.superType); - } - for (DexType iface : clazz.interfaces.values) { - if (seen.add(iface)) { - worklist.add(iface); - } - } - } - } - } - @Override public void forEachInstantiatedSubType( DexType type,
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java index 33b077d..658cfbf 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -209,10 +209,6 @@ return this == FINAL_TREE_SHAKING; } - public boolean isInitialOrFinalTreeShaking() { - return isInitialTreeShaking() || isFinalTreeShaking(); - } - public boolean isInitialMainDexTracing() { return this == INITIAL_MAIN_DEX_TRACING; } @@ -480,7 +476,7 @@ ? ProguardCompatibilityActions.builder() : null; - if (mode.isInitialOrFinalTreeShaking()) { + if (mode.isTreeShaking()) { GetArrayOfMissingTypeVerifyErrorWorkaround.register(appView, this); InvokeVirtualToInterfaceVerifyErrorWorkaround.register(appView, this); if (options.protoShrinking().enableGeneratedMessageLiteShrinking) { @@ -817,15 +813,9 @@ return; } // Add everything if we are not shrinking. - assert appView.options().getProguardConfiguration().getKeepAllRule() != null; - ProguardKeepRuleBase keepAllRule = - appView.options().getProguardConfiguration().getKeepAllRule(); - KeepClassInfo.Joiner keepClassInfo = - KeepClassInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking(); - KeepFieldInfo.Joiner keepFieldInfo = - KeepFieldInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking(); - KeepMethodInfo.Joiner keepMethodInfo = - KeepMethodInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking(); + KeepClassInfo.Joiner keepClassInfo = KeepClassInfo.newEmptyJoiner().disallowShrinking(); + KeepFieldInfo.Joiner keepFieldInfo = KeepFieldInfo.newEmptyJoiner().disallowShrinking(); + KeepMethodInfo.Joiner keepMethodInfo = KeepMethodInfo.newEmptyJoiner().disallowShrinking(); EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get(); for (DexProgramClass clazz : appView.appInfo().classes()) { if (appView.getSyntheticItems().isSyntheticClass(clazz) @@ -845,8 +835,7 @@ DexProgramClass clazz, KeepClassInfo.Joiner minimumKeepInfo, EnqueuerEvent preconditionEvent) { - assert !minimumKeepInfo.isShrinkingAllowed(); - assert !minimumKeepInfo.getRules().isEmpty(); + assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options); DexDefinition precondition = preconditionEvent.getDefinition(appInfo()); enqueueKeepRuleInstantiatedType(clazz, minimumKeepInfo.getRules(), precondition); } @@ -876,8 +865,7 @@ private void enqueueFieldDueToNoShrinkingRule( ProgramField field, KeepFieldInfo.Joiner minimumKeepInfo, EnqueuerEvent preconditionEvent) { - assert !minimumKeepInfo.isShrinkingAllowed(); - assert !minimumKeepInfo.getRules().isEmpty(); + assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options); DexDefinition precondition = preconditionEvent.getDefinition(appInfo()); workList.enqueueMarkFieldKeptAction( field, @@ -889,8 +877,7 @@ ProgramMethod method, KeepMethodInfo.Joiner minimumKeepInfo, EnqueuerEvent preconditionEvent) { - assert !minimumKeepInfo.isShrinkingAllowed(); - assert !minimumKeepInfo.getRules().isEmpty(); + assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options); DexDefinition precondition = preconditionEvent.getDefinition(appInfo()); workList.enqueueMarkMethodKeptAction( method, @@ -2419,7 +2406,8 @@ // because each analysis may depend on seeing all the (clazz, reason) pairs. Thus, not doing so // could lead to nondeterminism. analyses.forEach( - analysis -> analysis.processNewlyInstantiatedClass(clazz.asProgramClass(), context)); + analysis -> + analysis.processNewlyInstantiatedClass(clazz.asProgramClass(), context, workList)); if (!markInstantiatedClass(clazz, context, instantiationReason, keepReason)) { return; @@ -2799,7 +2787,7 @@ applyMinimumKeepInfo(field); // Notify analyses. - analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context)); + analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context, workList)); } // Package protected due to entry point from worklist. @@ -2825,7 +2813,7 @@ traceFieldDefinition(field); - analyses.forEach(analysis -> analysis.notifyMarkFieldAsReachable(field)); + analyses.forEach(analysis -> analysis.notifyMarkFieldAsReachable(field, workList)); } private void traceFieldDefinition(ProgramField field) { @@ -3076,7 +3064,7 @@ target.accept( method -> markVirtualDispatchTargetAsLive(method, reason), lambda -> markVirtualDispatchTargetAsLive(lambda, reason)); - analyses.forEach(analysis -> analysis.notifyMarkVirtualDispatchTargetAsLive(target)); + analyses.forEach(analysis -> analysis.notifyMarkVirtualDispatchTargetAsLive(target, workList)); } private void markVirtualDispatchTargetAsLive( @@ -3156,7 +3144,9 @@ if (target == null) { failedMethodResolutionTargets.add(resolution.getResolvedMethod().getReference()); analyses.forEach( - analyses -> analyses.notifyFailedMethodResolutionTarget(resolution.getResolvedMethod())); + analyses -> + analyses.notifyFailedMethodResolutionTarget( + resolution.getResolvedMethod(), workList)); return; } @@ -3305,7 +3295,7 @@ KeepClassInfo.Joiner minimumKeepInfo) { if ((options.isShrinking() || mode.isMainDexTracing()) && !minimumKeepInfo.isShrinkingAllowed()) { - assert !minimumKeepInfo.getRules().isEmpty(); + assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options); enqueueClassDueToNoShrinkingRule(clazz, minimumKeepInfo, preconditionEvent); } } @@ -3352,7 +3342,7 @@ ProgramField field, EnqueuerEvent preconditionEvent, KeepFieldInfo.Joiner minimumKeepInfo) { if ((options.isShrinking() || mode.isMainDexTracing()) && !minimumKeepInfo.isShrinkingAllowed()) { - assert !minimumKeepInfo.getRules().isEmpty(); + assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options); enqueueFieldDueToNoShrinkingRule(field, minimumKeepInfo, preconditionEvent); } } @@ -3401,7 +3391,7 @@ KeepMethodInfo.Joiner minimumKeepInfo) { if ((options.isShrinking() || mode.isMainDexTracing()) && !minimumKeepInfo.isShrinkingAllowed()) { - assert !minimumKeepInfo.getRules().isEmpty(); + assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options); enqueueMethodDueToNoShrinkingRule(method, minimumKeepInfo, preconditionEvent); if (method.getDefinition().isInstanceInitializer()) { @@ -4317,7 +4307,7 @@ } // Notify analyses. - analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context)); + analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context, workList)); } private void markMethodAsTargeted(ProgramMethod method, KeepReason reason) { @@ -4337,7 +4327,7 @@ markMethodAsLiveWithCompatRule(method); } } - analyses.forEach(analysis -> analysis.notifyMarkMethodAsTargeted(method)); + analyses.forEach(analysis -> analysis.notifyMarkMethodAsTargeted(method, workList)); } void traceMethodDefinitionExcludingCode(ProgramMethod method) { @@ -4368,7 +4358,7 @@ useRegistryFactory.create(appView, method, this, appView.apiLevelCompute()); method.registerCodeReferences(registry); // Notify analyses. - analyses.forEach(analysis -> analysis.processTracedCode(method, registry)); + analyses.forEach(analysis -> analysis.processTracedCode(method, registry, workList)); } private void markReferencedTypesAsLive(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java index 83055e7..2fe6b81 100644 --- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java +++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -31,6 +31,7 @@ import com.android.tools.r8.references.Reference; import com.android.tools.r8.references.TypeReference; import com.android.tools.r8.utils.DequeUtils; +import com.android.tools.r8.utils.InternalOptions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.Sets; @@ -45,6 +46,7 @@ public class GraphReporter { private final AppView<?> appView; + private final InternalOptions options; private final GraphConsumer keptGraphConsumer; private final CollectingGraphConsumer verificationGraphConsumer; @@ -58,6 +60,7 @@ GraphReporter(AppView<?> appView, GraphConsumer keptGraphConsumer) { this.appView = appView; + this.options = appView.options(); if (appView.options().testing.verifyKeptGraphInfo) { this.verificationGraphConsumer = new CollectingGraphConsumer(keptGraphConsumer); this.keptGraphConsumer = verificationGraphConsumer; @@ -108,17 +111,17 @@ KeepReasonWitness reportKeepClass( DexDefinition precondition, ProguardKeepRuleBase rule, DexProgramClass clazz) { - if (keptGraphConsumer == null) { - return KeepReasonWitness.INSTANCE; + if (keptGraphConsumer != null) { + KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule); + EdgeKind edgeKind = reportPrecondition(ruleNode); + return reportEdge(ruleNode, getClassGraphNode(clazz.type), edgeKind); } - KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule); - EdgeKind edgeKind = reportPrecondition(ruleNode); - return reportEdge(ruleNode, getClassGraphNode(clazz.type), edgeKind); + return KeepReasonWitness.INSTANCE; } KeepReasonWitness reportKeepClass( DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexProgramClass clazz) { - assert !rules.isEmpty(); + assert !rules.isEmpty() || !options.isShrinking(); if (keptGraphConsumer != null) { for (ProguardKeepRuleBase rule : rules) { reportKeepClass(precondition, rule, clazz); @@ -129,17 +132,17 @@ KeepReasonWitness reportKeepMethod( DexDefinition precondition, ProguardKeepRuleBase rule, DexEncodedMethod method) { - if (keptGraphConsumer == null) { - return KeepReasonWitness.INSTANCE; + if (keptGraphConsumer != null) { + KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule); + EdgeKind edgeKind = reportPrecondition(ruleNode); + return reportEdge(ruleNode, getMethodGraphNode(method.getReference()), edgeKind); } - KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule); - EdgeKind edgeKind = reportPrecondition(ruleNode); - return reportEdge(ruleNode, getMethodGraphNode(method.getReference()), edgeKind); + return KeepReasonWitness.INSTANCE; } KeepReasonWitness reportKeepMethod( DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexEncodedMethod method) { - assert !rules.isEmpty(); + assert !rules.isEmpty() || !options.isShrinking(); if (keptGraphConsumer != null) { for (ProguardKeepRuleBase rule : rules) { reportKeepMethod(precondition, rule, method); @@ -150,17 +153,17 @@ KeepReasonWitness reportKeepField( DexDefinition precondition, ProguardKeepRuleBase rule, DexEncodedField field) { - if (keptGraphConsumer == null) { - return KeepReasonWitness.INSTANCE; + if (keptGraphConsumer != null) { + KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule); + EdgeKind edgeKind = reportPrecondition(ruleNode); + return reportEdge(ruleNode, getFieldGraphNode(field.getReference()), edgeKind); } - KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule); - EdgeKind edgeKind = reportPrecondition(ruleNode); - return reportEdge(ruleNode, getFieldGraphNode(field.getReference()), edgeKind); + return KeepReasonWitness.INSTANCE; } KeepReasonWitness reportKeepField( DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexEncodedField field) { - assert !rules.isEmpty(); + assert !rules.isEmpty() || !options.isShrinking(); if (keptGraphConsumer != null) { for (ProguardKeepRuleBase rule : rules) { reportKeepField(precondition, rule, field);
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java index 598f6a7..9b83643 100644 --- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java +++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -8,6 +8,7 @@ import com.android.tools.r8.graph.EnclosingMethodAttribute; import com.android.tools.r8.graph.ProgramDefinition; import com.android.tools.r8.shaking.KeepInfo.Builder; +import com.android.tools.r8.utils.InternalOptions; import com.google.common.collect.Sets; import java.util.Set; import java.util.function.Consumer; @@ -560,5 +561,11 @@ assert original.isLessThanOrEquals(joined); return joined; } + + public boolean verifyShrinkingDisallowedWithRule(InternalOptions options) { + assert !isShrinkingAllowed(); + assert !getRules().isEmpty() || !options.isShrinking(); + return true; + } } }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java index 2f6de9b..c1f5a95 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -10,7 +10,6 @@ import com.android.tools.r8.position.Position; import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode; import com.android.tools.r8.utils.Reporter; -import com.android.tools.r8.utils.StringUtils; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import java.nio.file.Path; @@ -68,12 +67,10 @@ ProguardPathFilter.builder().disable(); private boolean forceProguardCompatibility = false; private boolean overloadAggressively; - private boolean keepRuleSynthesisForRecompilation = false; private boolean configurationDebugging = false; private boolean dontUseMixedCaseClassnames = false; private boolean protoShrinking = false; private int maxRemovedAndroidLogLevel = 1; - private ProguardKeepRule keepAllRule; private Builder(DexItemFactory dexItemFactory, Reporter reporter) { this.dexItemFactory = dexItemFactory; @@ -281,10 +278,6 @@ this.overloadAggressively = overloadAggressively; } - public void enableKeepRuleSynthesisForRecompilation() { - this.keepRuleSynthesisForRecompilation = true; - } - public void setConfigurationDebugging(boolean configurationDebugging) { this.configurationDebugging = configurationDebugging; } @@ -309,24 +302,6 @@ this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel; } - /** - * This synthesizes a set of keep rules that are necessary in order to be able to successfully - * recompile the generated dex files with the same keep rules. - */ - public void synthesizeKeepRulesForRecompilation() { - List<ProguardConfigurationRule> synthesizedKeepRules = new ArrayList<>(); - for (ProguardConfigurationRule rule : rules) { - ProguardConfigurationUtils.synthesizeKeepRulesForRecompilation(rule, synthesizedKeepRules); - } - if (rules.addAll(synthesizedKeepRules)) { - parsedConfiguration.add( - StringUtils.lines( - synthesizedKeepRules.stream() - .map(ProguardClassSpecification::toString) - .toArray(String[]::new))); - } - } - public ProguardConfiguration buildRaw() { ProguardConfiguration configuration = new ProguardConfiguration( @@ -369,8 +344,7 @@ configurationDebugging, dontUseMixedCaseClassnames, protoShrinking, - maxRemovedAndroidLogLevel, - keepAllRule); + maxRemovedAndroidLogLevel); reporter.failIfPendingErrors(); @@ -382,31 +356,6 @@ // For Proguard -keepattributes are only applicable when obfuscating. keepAttributePatterns.addAll(ProguardKeepAttributes.KEEP_ALL); } - // If either of the flags -dontshrink or -dontobfuscate, or shrinking or minification is - // turned off through the API, then add a match all rule which will apply that. - if (!isObfuscating() || !isOptimizing() || !isShrinking()) { - ProguardKeepRule rule = - ProguardKeepRule.defaultKeepAllRule( - modifiers -> { - modifiers - .setAllowsAccessModification(isAccessModificationEnabled()) - .setAllowsObfuscation(isObfuscating()) - .setAllowsOptimization(isOptimizing()) - .setAllowsShrinking(isShrinking()); - - // In non-compatibility mode, adding -dontoptimize does not cause all annotations - // to be retained. - if (!forceProguardCompatibility && isShrinking()) { - modifiers.setAllowsAnnotationRemoval(true); - } - }); - addRule(rule); - this.keepAllRule = rule; - } - - if (keepRuleSynthesisForRecompilation) { - synthesizeKeepRulesForRecompilation(); - } if (packageObfuscationMode == PackageObfuscationMode.NONE && obfuscating) { packageObfuscationMode = PackageObfuscationMode.MINIFICATION; @@ -456,7 +405,6 @@ private final boolean dontUseMixedCaseClassnames; private final boolean protoShrinking; private final int maxRemovedAndroidLogLevel; - private final ProguardKeepRule keepAllRule; private ProguardConfiguration( String parsedConfiguration, @@ -498,8 +446,7 @@ boolean configurationDebugging, boolean dontUseMixedCaseClassnames, boolean protoShrinking, - int maxRemovedAndroidLogLevel, - ProguardKeepRule keepAllRule) { + int maxRemovedAndroidLogLevel) { this.parsedConfiguration = parsedConfiguration; this.dexItemFactory = factory; this.injars = ImmutableList.copyOf(injars); @@ -540,7 +487,6 @@ this.dontUseMixedCaseClassnames = dontUseMixedCaseClassnames; this.protoShrinking = protoShrinking; this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel; - this.keepAllRule = keepAllRule; } /** @@ -720,10 +666,6 @@ return maxRemovedAndroidLogLevel; } - public ProguardKeepRule getKeepAllRule() { - return keepAllRule; - } - @Override public String toString() { StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java index 8c0d0b4..bf21d1f 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -181,21 +181,4 @@ } return false; } - - public static void synthesizeKeepRulesForRecompilation( - ProguardConfigurationRule rule, List<ProguardConfigurationRule> synthesizedKeepRules) { - if (rule.hasInheritanceClassName()) { - ProguardTypeMatcher inheritanceClassName = rule.getInheritanceClassName(); - synthesizedKeepRules.add( - ProguardKeepRule.builder() - .setOrigin(synthesizedRecompilationOrigin) - .setType(ProguardKeepRuleType.KEEP) - .setClassType( - rule.getInheritanceIsExtends() - ? ProguardClassType.CLASS - : ProguardClassType.INTERFACE) - .setClassNames(ProguardClassNameList.singletonList(inheritanceClassName)) - .build()); - } - } }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java index 089ccce..8eb6b51 100644 --- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java +++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -11,22 +11,44 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.LibraryMethod; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter; +import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter; public class AndroidApiLevelUtils { public static boolean isApiSafeForInlining( ProgramMethod caller, ProgramMethod inlinee, InternalOptions options) { + return isApiSafeForInlining( + caller, inlinee, options, NopWhyAreYouNotInliningReporter.getInstance()); + } + + public static boolean isApiSafeForInlining( + ProgramMethod caller, + ProgramMethod inlinee, + InternalOptions options, + WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { if (!options.apiModelingOptions().enableApiCallerIdentification) { return true; } if (caller.getHolderType() == inlinee.getHolderType()) { return true; } + ComputedApiLevel callerApiLevelForCode = caller.getDefinition().getApiLevelForCode(); + if (callerApiLevelForCode.isUnknownApiLevel()) { + whyAreYouNotInliningReporter.reportCallerHasUnknownApiLevel(); + return false; + } // For inlining we only measure if the code has invokes into the library. - return caller + ComputedApiLevel inlineeApiLevelForCode = inlinee.getDefinition().getApiLevelForCode(); + if (!caller .getDefinition() .getApiLevelForCode() - .isGreaterThanOrEqualTo(inlinee.getDefinition().getApiLevelForCode()); + .isGreaterThanOrEqualTo(inlineeApiLevelForCode)) { + whyAreYouNotInliningReporter.reportInlineeHigherApiCall( + callerApiLevelForCode, inlineeApiLevelForCode); + return false; + } + return true; } public static ComputedApiLevel getApiReferenceLevelForMerging(
diff --git a/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java b/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java index 7dc7b33..df3f944 100644 --- a/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java +++ b/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
@@ -11,20 +11,20 @@ public class AssertionConfigurationWithDefault { - public final AssertionTransformation defautlTransformation; + public final AssertionsConfiguration defaultConfiguration; public final List<AssertionsConfiguration> assertionsConfigurations; public AssertionConfigurationWithDefault( - AssertionTransformation defautlTransformation, + AssertionsConfiguration defautlTransformation, List<AssertionsConfiguration> assertionsConfigurations) { - this.defautlTransformation = defautlTransformation; + this.defaultConfiguration = defautlTransformation; assert assertionsConfigurations != null; this.assertionsConfigurations = assertionsConfigurations; } public boolean isPassthroughAll() { if (assertionsConfigurations.size() == 0) { - return defautlTransformation == AssertionTransformation.PASSTHROUGH; + return defaultConfiguration.getTransformation() == AssertionTransformation.PASSTHROUGH; } return assertionsConfigurations.size() == 1 && assertionsConfigurations.get(0).getScope() == AssertionTransformationScope.ALL
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanUtils.java b/src/main/java/com/android/tools/r8/utils/BooleanUtils.java index e99ff55..4df05c6 100644 --- a/src/main/java/com/android/tools/r8/utils/BooleanUtils.java +++ b/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
@@ -28,4 +28,8 @@ public static Boolean[] falseValues() { return FALSE_VALUES; } + + public static boolean xor(boolean x, boolean y) { + return (x && !y) || (!x && y); + } }
diff --git a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java index 4f652cf..4b20c06 100644 --- a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java +++ b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
@@ -6,6 +6,7 @@ import java.util.Set; import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -33,6 +34,11 @@ }; } + public static <S, T, R> BiConsumer<S, T> andThen( + BiFunction<S, T, R> function, Consumer<R> consumer) { + return (s, t) -> consumer.accept(function.apply(s, t)); + } + public static <T> Consumer<T> emptyConsumer() { return ignore -> {}; }
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 d32fe5c..bc88b16 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -18,7 +18,6 @@ import com.android.tools.r8.SourceFileProvider; import com.android.tools.r8.StringConsumer; import com.android.tools.r8.Version; -import com.android.tools.r8.androidapi.AndroidApiForHashingClass; import com.android.tools.r8.androidapi.ComputedApiLevel; import com.android.tools.r8.cf.CfVersion; import com.android.tools.r8.dex.Marker; @@ -39,12 +38,12 @@ import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexClasspathClass; import com.android.tools.r8.graph.DexEncodedMethod; -import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItem; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexLibraryClass; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses; @@ -780,10 +779,6 @@ return desugarSpecificOptions; } - public static boolean shouldEnableKeepRuleSynthesisForRecompilation() { - return System.getProperty("com.android.tools.r8.keepRuleSynthesisForRecompilation") != null; - } - private static Set<String> getExtensiveLoggingFilter() { String property = System.getProperty("com.android.tools.r8.extensiveLoggingFilter"); if (property != null) { @@ -1489,12 +1484,6 @@ public static class ApiModelTestingOptions { - // A mapping from references to the api-level introducing them. - public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>(); - public Map<FieldReference, AndroidApiLevel> fieldApiMapping = new HashMap<>(); - public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>(); - public BiConsumer<MethodReference, ComputedApiLevel> tracedMethodApiLevelCallback = null; - public boolean enableApiCallerIdentification = System.getProperty("com.android.tools.r8.disableApiModeling") == null; public boolean checkAllApiReferencesAreSet = @@ -1504,54 +1493,28 @@ public boolean enableOutliningOfMethods = System.getProperty("com.android.tools.r8.disableApiModeling") == null; + // A mapping from references to the api-level introducing them. + public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>(); + public Map<FieldReference, AndroidApiLevel> fieldApiMapping = new HashMap<>(); + public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>(); + public BiConsumer<MethodReference, ComputedApiLevel> tracedMethodApiLevelCallback = null; + public void visitMockedApiLevelsForReferences( - DexItemFactory factory, Consumer<AndroidApiForHashingClass> consumer) { + DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) { if (methodApiMapping.isEmpty() && fieldApiMapping.isEmpty() && classApiMapping.isEmpty()) { return; } - Set<ClassReference> classReferences = new HashSet<>(classApiMapping.keySet()); - methodApiMapping - .keySet() - .forEach(methodReference -> classReferences.add(methodReference.getHolderClass())); - fieldApiMapping - .keySet() - .forEach(methodReference -> classReferences.add(methodReference.getHolderClass())); - classReferences.forEach( - classReference -> { - consumer.accept( - new AndroidApiForHashingClass() { - @Override - public DexType getType() { - return factory.createType(classReference.getDescriptor()); - } - - @Override - public AndroidApiLevel getApiLevel() { - return classApiMapping.getOrDefault(classReference, ANDROID_PLATFORM); - } - - @Override - public void visitMethodsWithApiLevels( - BiConsumer<DexMethod, AndroidApiLevel> consumer) { - methodApiMapping.forEach( - (methodReference, apiLevel) -> { - if (methodReference.getHolderClass().equals(classReference)) { - consumer.accept(factory.createMethod(methodReference), apiLevel); - } - }); - } - - @Override - public void visitFieldsWithApiLevels( - BiConsumer<DexField, AndroidApiLevel> consumer) { - fieldApiMapping.forEach( - (fieldReference, apiLevel) -> { - if (fieldReference.getHolderClass().equals(classReference)) { - consumer.accept(factory.createField(fieldReference), apiLevel); - } - }); - } - }); + classApiMapping.forEach( + (classReference, apiLevel) -> { + apiLevelConsumer.accept(factory.createType(classReference.getDescriptor()), apiLevel); + }); + fieldApiMapping.forEach( + (fieldReference, apiLevel) -> { + apiLevelConsumer.accept(factory.createField(fieldReference), apiLevel); + }); + methodApiMapping.forEach( + (methodReference, apiLevel) -> { + apiLevelConsumer.accept(factory.createMethod(methodReference), apiLevel); }); }
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java index 624fb10..6be9cc3 100644 --- a/src/main/java/com/android/tools/r8/utils/SetUtils.java +++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -60,7 +60,7 @@ return result; } - public static <T> Set<T> newIdentityHashSet(Iterable<T> c) { + public static <T> Set<T> newIdentityHashSet(Iterable<? extends T> c) { Set<T> result = Sets.newIdentityHashSet(); c.forEach(result::add); return result;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java index 8531ceb..97ee26b 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -116,16 +116,16 @@ assertFalse(synthesizedMissingNotReferenced.isPresent()); verifyThat(inspector, parameters, addedOn23).isNotOutlinedFrom(testMethod); verifyThat(inspector, parameters, addedOn27) - .isOutlinedFromUntil(testMethod, AndroidApiLevel.O_MR1); + .isOutlinedFromUntil(testMethod, finalLibraryMethodLevel); verifyThat( inspector, parameters, LibraryClass.class.getDeclaredMethod("missingAndReferenced")) - .isOutlinedFrom(testMethod); - if (parameters.getApiLevel().isLessThan(AndroidApiLevel.O_MR1)) { - assertEquals(5, inspector.allClasses().size()); - } else { + .isNotOutlinedFrom(testMethod); + if (parameters.getApiLevel().isLessThan(finalLibraryMethodLevel)) { assertEquals(4, inspector.allClasses().size()); + } else { + assertEquals(3, inspector.allClasses().size()); } }); }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java new file mode 100644 index 0000000..f8b3bf7 --- /dev/null +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
@@ -0,0 +1,87 @@ +// 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.apimodel; + +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; +import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeFalse; + +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.testing.AndroidBuildVersion; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.codeinspector.FoundClassSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class ApiModelOutlineMethodUnknownTest extends TestBase { + + private static final AndroidApiLevel classApiLevel = AndroidApiLevel.M; + + @Parameter public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void testR8() throws Exception { + // TODO(b/197078995): Make this work on 12. + assumeFalse( + parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0)); + boolean willInvokeLibraryMethods = + parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel); + testForR8(parameters.getBackend()) + .addLibraryClasses(LibraryClass.class) + .addDefaultRuntimeLibrary(parameters) + .addProgramClasses(Main.class) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .addAndroidBuildVersion() + .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel)) + .apply(ApiModelingTestHelper::enableOutliningOfMethods) + .apply(ApiModelingTestHelper::disableStubbingOfClasses) + .compile() + .inspect( + inspector -> { + // Assert that we did not outline any methods. + assertEquals( + 0, + inspector.allClasses().stream() + .filter(FoundClassSubject::isCompilerSynthesized) + .count()); + }) + .applyIf(willInvokeLibraryMethods, b -> b.addBootClasspathClasses(LibraryClass.class)) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods, "Not calling API") + .assertSuccessWithOutputLinesIf(willInvokeLibraryMethods, "LibraryClass::unknownApiLevel"); + } + + // Only present from api level 23. + public static class LibraryClass { + + public static void unknownApiLevel() { + System.out.println("LibraryClass::unknownApiLevel"); + } + } + + public static class Main { + + public static void main(String[] args) { + if (AndroidBuildVersion.VERSION >= 23) { + LibraryClass.unknownApiLevel(); + } else { + System.out.println("Not calling API"); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java new file mode 100644 index 0000000..40b302d --- /dev/null +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
@@ -0,0 +1,135 @@ +// 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.apimodel; + +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; +import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.D8TestCompileResult; +import com.android.tools.r8.SingleTestRunResult; +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.testing.AndroidBuildVersion; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.codeinspector.FoundClassSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class ApiModelOutlinePackagePrivateTest extends TestBase { + + private static final AndroidApiLevel classApiLevel = AndroidApiLevel.G; + private static final AndroidApiLevel methodApiLevel = AndroidApiLevel.G_MR1; + + @Parameter public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private boolean willInvokeLibraryMethods() { + return parameters.isDexRuntime() + && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel); + } + + @Test + public void testD8BootClassPath() throws Exception { + assumeTrue(parameters.isDexRuntime()); + assumeTrue(parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + compileOnD8() + .addBootClasspathClasses(LibraryClass.class) + .run(parameters.getRuntime(), Main.class) + .apply(this::checkResultOnBootClassPath); + } + + @Test + public void testD8RuntimeClasspath() throws Exception { + assumeTrue(parameters.isDexRuntime()); + compileOnD8() + .addRunClasspathClasses(LibraryClass.class) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods(), "Not calling API") + .assertSuccessWithOutputLinesIf(willInvokeLibraryMethods(), "LibraryClass::addedOn10"); + } + + private D8TestCompileResult compileOnD8() throws Exception { + return testForD8(parameters.getBackend()) + .addLibraryClasses(LibraryClass.class) + .addProgramClasses(Main.class) + .addAndroidBuildVersion() + .setMinApi(parameters.getApiLevel()) + .compile(); + } + + @Test + public void testR8() throws Exception { + // TODO(b/197078995): Make this work on 12. + assumeFalse( + parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0)); + testForR8(parameters.getBackend()) + .addLibraryClasses(LibraryClass.class) + .addDefaultRuntimeLibrary(parameters) + .addProgramClasses(Main.class) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .addKeepAttributeInnerClassesAndEnclosingMethod() + .addAndroidBuildVersion() + .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel)) + .apply( + setMockApiLevelForMethod( + LibraryClass.class.getDeclaredMethod("addedOn10"), methodApiLevel)) + .apply(ApiModelingTestHelper::enableOutliningOfMethods) + .apply(ApiModelingTestHelper::disableStubbingOfClasses) + .compile() + .inspect( + inspector -> { + // Assert that we did not outline any methods. + assertEquals( + 0, + inspector.allClasses().stream() + .filter(FoundClassSubject::isCompilerSynthesized) + .count()); + }) + .applyIf(willInvokeLibraryMethods(), b -> b.addBootClasspathClasses(LibraryClass.class)) + .run(parameters.getRuntime(), Main.class) + .apply(this::checkResultOnBootClassPath); + } + + private void checkResultOnBootClassPath(SingleTestRunResult<?> runResult) { + runResult + .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods(), "Not calling API") + // We are expecting an IllegalAccessError since LibraryClass is on bootclasspath and + // for Main to call the package private method it has to be loaded by the same class loader. + .assertFailureWithErrorThatThrowsIf(willInvokeLibraryMethods(), IllegalAccessError.class); + } + + // Only present from api level 9. + public static class LibraryClass { + + static void addedOn10() { + System.out.println("LibraryClass::addedOn10"); + } + } + + public static class Main { + + public static void main(String[] args) { + if (AndroidBuildVersion.VERSION >= 10) { + LibraryClass.addedOn10(); + } else { + System.out.println("Not calling API"); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java index 06bfb5b..e5a169b 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoHorizontalClassMerging; +import com.android.tools.r8.NoMethodStaticizing; import com.android.tools.r8.TestParameters; import com.android.tools.r8.horizontalclassmerging.ClassMerger; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -36,6 +37,7 @@ .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoHorizontalClassMergingAnnotations() + .enableNoMethodStaticizingAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutputLines("changed", "0", "42", "foo", "7", "foo", "print a", "print b") @@ -71,6 +73,7 @@ @NoHorizontalClassMerging public static class Parent { @NeverInline + @NoMethodStaticizing public void foo() { System.out.println("foo"); }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java index 1f413bc..02e26dc 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoMethodStaticizing; import com.android.tools.r8.TestParameters; import org.junit.Test; @@ -27,6 +28,7 @@ .allowStdoutMessages() .enableInliningAnnotations() .enableNeverClassInliningAnnotations() + .enableNoMethodStaticizingAnnotations() .setMinApi(parameters.getApiLevel()) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertMergedInto(B.class, A.class)) @@ -42,6 +44,7 @@ public interface Interface { @NeverInline + @NoMethodStaticizing default void print() { System.out.println("print interface"); }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonTrivialClassInitializationAfterMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonTrivialClassInitializationAfterMergingTest.java new file mode 100644 index 0000000..d5bbb8e --- /dev/null +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonTrivialClassInitializationAfterMergingTest.java
@@ -0,0 +1,79 @@ +// 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.classmerging.horizontal; + +import com.android.tools.r8.NoHorizontalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class NonTrivialClassInitializationAfterMergingTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addKeepClassAndMembersRules(I.class) + .addHorizontallyMergedClassesInspector( + inspector -> + inspector + .applyIf( + !parameters.canUseDefaultAndStaticInterfaceMethods(), + i -> i.assertIsCompleteMergeGroup(A.class, B.class)) + .assertNoOtherClassesMerged()) + .enableNoHorizontalClassMergingAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), Main.class) + .applyIf( + parameters.canUseDefaultAndStaticInterfaceMethods(), + runResult -> runResult.assertSuccessWithOutputLines("Hello world!"), + runResult -> runResult.assertSuccessWithOutput("Hello")); + } + + static class Main { + + public static void main(String[] args) { + new A(); + System.out.print("Hello"); + new B(); + } + } + + interface I { + + Greeter greeter = new Greeter(); + + default void foo() {} + } + + static class A {} + + // Prints " world!" when initialized due to implementing I. + static class B implements I {} + + @NoHorizontalClassMerging + static class Greeter { + + static { + System.out.println(" world!"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java index 100567d..33aab91 100644 --- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java +++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
@@ -6,6 +6,7 @@ import static org.junit.Assert.assertEquals; import com.android.tools.r8.compilerapi.mockdata.MockClass; +import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -70,6 +71,10 @@ return MockClass.class; } + public Class<?> getMockClassWithAssertion() { + return MockClassWithAssertion.class; + } + public Path getJava8RuntimeJar() { return Paths.get("third_party", "openjdk", "openjdk-rt-1.8", "rt.jar"); }
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java index c2fab32..24e1de1 100644 --- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java +++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -8,8 +8,10 @@ import static com.android.tools.r8.ToolHelper.isTestingR8Lib; import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.compilerapi.assertionconfiguration.AssertionConfigurationTest; import com.android.tools.r8.compilerapi.mapid.CustomMapIdTest; import com.android.tools.r8.compilerapi.mockdata.MockClass; +import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion; import com.android.tools.r8.compilerapi.sourcefile.CustomSourceFileTest; import com.android.tools.r8.compilerapi.testsetup.ApiTestingSetUpTest; import com.google.common.collect.ImmutableList; @@ -26,10 +28,13 @@ Paths.get(ToolHelper.THIRD_PARTY_DIR, "binary_compatibility_tests", DIRNAME, "tests.jar"); private static final List<Class<? extends CompilerApiTest>> CLASSES_FOR_BINARY_COMPATIBILITY = - ImmutableList.of(ApiTestingSetUpTest.ApiTest.class); + ImmutableList.of( + ApiTestingSetUpTest.ApiTest.class, + CustomMapIdTest.ApiTest.class, + CustomSourceFileTest.ApiTest.class); private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY = - ImmutableList.of(CustomMapIdTest.ApiTest.class, CustomSourceFileTest.ApiTest.class); + ImmutableList.of(AssertionConfigurationTest.ApiTest.class); private final TemporaryFolder temp; @@ -54,7 +59,7 @@ @Override public List<Class<?>> getAdditionalClassesForTests() { - return ImmutableList.of(CompilerApiTest.class, MockClass.class); + return ImmutableList.of(CompilerApiTest.class, MockClass.class, MockClassWithAssertion.class); } @Override
diff --git a/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java b/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java new file mode 100644 index 0000000..aa85988 --- /dev/null +++ b/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java
@@ -0,0 +1,138 @@ +// 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.compilerapi.assertionconfiguration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.AssertionsConfiguration; +import com.android.tools.r8.D8; +import com.android.tools.r8.D8Command; +import com.android.tools.r8.DexIndexedConsumer; +import com.android.tools.r8.ProgramConsumer; +import com.android.tools.r8.R8; +import com.android.tools.r8.R8Command; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.compilerapi.CompilerApiTest; +import com.android.tools.r8.compilerapi.CompilerApiTestRunner; +import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.references.MethodReference; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.utils.ThrowingBiConsumer; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject; +import java.nio.file.Path; +import org.junit.Test; + +public class AssertionConfigurationTest extends CompilerApiTestRunner { + + static MethodReference assertionHandler = + Reference.methodFromDescriptor( + "Lcom/example/SomeClass;", "assertionHandler", "(Ljava/lang/AssertionError;)V"); + + public AssertionConfigurationTest(TestParameters parameters) { + super(parameters); + } + + @Override + public Class<? extends CompilerApiTest> binaryTestClass() { + return ApiTest.class; + } + + @Test + public void testD8() throws Exception { + ApiTest test = new ApiTest(ApiTest.PARAMETERS); + assertEquals(assertionHandler, ApiTest.assertionHandler); + runTest(test::runD8); + } + + @Test + public void testR8() throws Exception { + ApiTest test = new ApiTest(ApiTest.PARAMETERS); + runTest(test::runR8); + } + + private boolean invokesAssertionHandler(InstructionSubject instruction) { + return instruction.isInvokeStatic() + && ((InvokeInstructionSubject) instruction) + .invokedMethod() + .asMethodReference() + .equals(assertionHandler); + } + + private void runTest(ThrowingBiConsumer<ProgramConsumer, MethodReference, Exception> test) + throws Exception { + Path output = temp.newFolder().toPath().resolve("out.jar"); + test.accept(new DexIndexedConsumer.ArchiveConsumer(output), assertionHandler); + + // TODO(b/209445989): This should be true when the assertion handler support is implemented. + assertFalse( + new CodeInspector(output) + .clazz(MockClassWithAssertion.class) + .uniqueMethodWithName("main") + .streamInstructions() + .anyMatch(this::invokesAssertionHandler)); + } + + public static class ApiTest extends CompilerApiTest { + + static MethodReference assertionHandler = + Reference.methodFromDescriptor( + "Lcom/example/SomeClass;", "assertionHandler", "(Ljava/lang/AssertionError;)V"); + + public ApiTest(Object parameters) { + super(parameters); + } + + AssertionsConfiguration buildWithAssertionHandler(AssertionsConfiguration.Builder builder) { + AssertionsConfiguration configuration = + builder.setAssertionHandler(assertionHandler).setScopeAll().build(); + assertFalse(configuration.isCompileTimeEnabled()); + assertFalse(configuration.isCompileTimeDisabled()); + assertFalse(configuration.isPassthrough()); + assertTrue(configuration.isAssertionHandler()); + assertSame(assertionHandler, configuration.getAssertionHandler()); + return configuration; + } + + public void runD8(ProgramConsumer programConsumer, MethodReference assertionHandler) + throws Exception { + D8.run( + D8Command.builder() + .addClassProgramData(getBytesForClass(getMockClassWithAssertion()), Origin.unknown()) + .addLibraryFiles(getJava8RuntimeJar()) + .addAssertionsConfiguration(this::buildWithAssertionHandler) + .setProgramConsumer(programConsumer) + .build()); + } + + public void runR8(ProgramConsumer programConsumer, MethodReference assertionHandler) + throws Exception { + R8.run( + R8Command.builder() + .addClassProgramData(getBytesForClass(getMockClassWithAssertion()), Origin.unknown()) + .addProguardConfiguration( + getKeepMainRules(getMockClassWithAssertion()), Origin.unknown()) + .addLibraryFiles(getJava8RuntimeJar()) + .addAssertionsConfiguration( + builder -> builder.setAssertionHandler(assertionHandler).setScopeAll().build()) + .setProgramConsumer(programConsumer) + .build()); + } + + @Test + public void testD8() throws Exception { + runD8(DexIndexedConsumer.emptyConsumer(), assertionHandler); + } + + @Test + public void testR8() throws Exception { + runR8(DexIndexedConsumer.emptyConsumer(), assertionHandler); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClassWithAssertion.java b/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClassWithAssertion.java new file mode 100644 index 0000000..df42fbd --- /dev/null +++ b/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClassWithAssertion.java
@@ -0,0 +1,12 @@ +// 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.compilerapi.mockdata; + +// Class to use as data for the compilation. +public class MockClassWithAssertion { + + public static void main(String[] args) { + assert false; + } +}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java b/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java new file mode 100644 index 0000000..3f384d4 --- /dev/null +++ b/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java
@@ -0,0 +1,105 @@ +// 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 org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.code.Instruction; +import com.android.tools.r8.graph.DexCode; +import com.android.tools.r8.graph.DexDebugEntry; +import com.android.tools.r8.graph.DexDebugEntryBuilder; +import com.android.tools.r8.graph.DexDebugInfo; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.utils.StringUtils; +import java.nio.file.Path; +import java.util.Iterator; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class Regress216178582Test extends TestBase { + + static final String EXPECTED = StringUtils.lines("Hello world!"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDexRuntimes().withAllApiLevels().build(); + } + + public Regress216178582Test(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + Path out = + testForR8(parameters.getBackend()) + .addInnerClasses(Regress216178582Test.class) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(TestClass.class) + .addKeepAttributeLineNumberTable() + .compile() + .inspect( + inspector -> { + DexEncodedMethod method = + inspector.clazz(TestClass.class).mainMethod().getMethod(); + DexCode code = method.getCode().asDexCode(); + if (parameters + .getApiLevel() + .isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport())) { + assertNull(code.getDebugInfo()); + return; + } + assertTrue(code.getDebugInfo().isPcBasedInfo()); + // Force convert the PC info to events. + code.setDebugInfo(DexDebugInfo.convertToEventBased(code, inspector.getFactory())); + List<DexDebugEntry> entries = + new DexDebugEntryBuilder(method, inspector.getFactory()).build(); + Iterator<DexDebugEntry> it = entries.iterator(); + int pc = 0; + for (Instruction instruction : code.instructions) { + if (instruction.canThrow()) { + DexDebugEntry next = it.next(); + assertEquals( + "Invalid entry " + + next + + " at pc " + + StringUtils.hexString(pc, 2) + + ", in:\n" + + method.codeToString(), + pc, + next.address); + } + pc += instruction.getSize(); + } + }) + .writeToZip(); + + testForD8(parameters.getBackend()) + .addProgramFiles(out) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(o -> o.testing.forceJumboStringProcessing = true) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + static class TestClass { + + // Use synchronized which will require prolog and epilog of monitor instructions. + // These have small sizes and debug event entries which hit a PC between instructions showing + // that the event stream is built incorrectly when processing jumbo strings. + public static synchronized void main(String[] args) { + System.out.println("Hello world!"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java index d2d8c86..f92b58e 100644 --- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java +++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.desugaring.interfacemethods; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import com.android.tools.r8.TestBase; @@ -36,9 +35,6 @@ .addProgramClasses(Condition.class) .noTreeShaking() .compile() - .inspectProguardConfiguration(p -> { - assertNotNull(p.getKeepAllRule()); - }) .inspect(i -> assertTrue(i.clazz(Condition.class).isAbstract())); }
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java index b0be3f7..2b5ccbd 100644 --- a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java +++ b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
@@ -22,13 +22,33 @@ public void testCombineRewritten() { DexItemFactory factory = new DexItemFactory(); ArgumentInfoCollection.Builder builder1 = ArgumentInfoCollection.builder(); - builder1.addArgumentInfo(1, new RewrittenTypeInfo(factory.intType, factory.longType)); - builder1.addArgumentInfo(3, new RewrittenTypeInfo(factory.intType, factory.longType)); + builder1.addArgumentInfo( + 1, + RewrittenTypeInfo.builder() + .setOldType(factory.intType) + .setNewType(factory.longType) + .build()); + builder1.addArgumentInfo( + 3, + RewrittenTypeInfo.builder() + .setOldType(factory.intType) + .setNewType(factory.longType) + .build()); ArgumentInfoCollection arguments1 = builder1.build(); ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder(); - builder2.addArgumentInfo(2, new RewrittenTypeInfo(factory.floatType, factory.doubleType)); - builder2.addArgumentInfo(4, new RewrittenTypeInfo(factory.floatType, factory.doubleType)); + builder2.addArgumentInfo( + 2, + RewrittenTypeInfo.builder() + .setOldType(factory.floatType) + .setNewType(factory.doubleType) + .build()); + builder2.addArgumentInfo( + 4, + RewrittenTypeInfo.builder() + .setOldType(factory.floatType) + .setNewType(factory.doubleType) + .build()); ArgumentInfoCollection arguments2 = builder2.build(); ArgumentInfoCollection combine = arguments1.combine(arguments2); @@ -112,8 +132,18 @@ ArgumentInfoCollection arguments1 = builder1.build(); ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder(); - builder2.addArgumentInfo(1, new RewrittenTypeInfo(factory.floatType, factory.doubleType)); - builder2.addArgumentInfo(2, new RewrittenTypeInfo(factory.floatType, factory.doubleType)); + builder2.addArgumentInfo( + 1, + RewrittenTypeInfo.builder() + .setOldType(factory.floatType) + .setNewType(factory.doubleType) + .build()); + builder2.addArgumentInfo( + 2, + RewrittenTypeInfo.builder() + .setOldType(factory.floatType) + .setNewType(factory.doubleType) + .build()); ArgumentInfoCollection arguments2 = builder2.build(); ArgumentInfoCollection combine = arguments1.combine(arguments2);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java index 65cc34d..b67037d 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java
@@ -72,10 +72,12 @@ ClassSubject bClassSubject = inspector.clazz(B.class); assertThat(bClassSubject.uniqueMethodWithName("kept"), isPresent()); - // A.notKept() and B.notKept() should not be present, because the only invoke instruction - // targeting A.notKept() should have been inlined. + // A.notKept() should not be present, because the only invoke instruction targeting A.notKept() + // should have been inlined. assertThat(aClassSubject.uniqueMethodWithName("notKept"), not(isPresent())); - assertThat(bClassSubject.uniqueMethodWithName("notKept"), not(isPresent())); + // B.notKept() is present because the caller has an invoke to an unknown method giving it api + // level unknown. + assertThat(bClassSubject.uniqueMethodWithName("notKept"), isPresent()); } static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java index 89586d8..20adeda 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java
@@ -70,10 +70,12 @@ ClassSubject aClassSubject = inspector.clazz(A.class); assertThat(aClassSubject.uniqueMethodWithName("kept"), isPresent()); - // I.notKept() and A.notKept() should not be present, because the only invoke instruction - // targeting I.notKept() should have been inlined. + // I.notKept() should not be present, because the only invoke instruction targeting I.notKept() + // should have been inlined. assertThat(iClassSubject.uniqueMethodWithName("notKept"), not(isPresent())); - assertThat(aClassSubject.uniqueMethodWithName("notKept"), not(isPresent())); + // A.notKept() is present because the caller has an invoke to an unknown method giving it api + // level unknown. + assertThat(aClassSubject.uniqueMethodWithName("notKept"), isPresent()); } static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java index 0c4e336..c0d3be4 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
@@ -5,7 +5,6 @@ package com.android.tools.r8.ir.optimize.membervaluepropagation; import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; -import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import com.android.tools.r8.NoHorizontalClassMerging; @@ -13,7 +12,6 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ir.optimize.membervaluepropagation.FieldReadForWriteTest.R.anim; -import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; import org.junit.Test; import org.junit.runner.RunWith; @@ -42,16 +40,7 @@ .enableNoHorizontalClassMergingAnnotations() .setMinApi(parameters.getApiLevel()) .compile() - .inspect( - inspector -> { - ClassSubject animClassSubject = inspector.clazz(anim.class); - if (parameters.isCfRuntime()) { - assertThat(animClassSubject, isPresent()); - assertThat(animClassSubject.uniqueFieldWithName("abc_fade_in"), isPresent()); - } else { - assertThat(animClassSubject, isAbsent()); - } - }); + .inspect(inspector -> assertThat(inspector.clazz(anim.class), isAbsent())); } static class Main {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationTest.java new file mode 100644 index 0000000..b5c1de5 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationTest.java
@@ -0,0 +1,130 @@ +// 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.ir.optimize.redundantarraygetelimination; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class RedundantArrayGetEliminationTest extends TestBase { + + private static final String EXPECTED_OUTPUT = StringUtils.lines("0", "0", "0", "42", "84"); + + @Parameter(0) + public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + testForD8(parameters.getBackend()) + .addInnerClasses(getClass()) + .release() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_OUTPUT); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepClassAndMembersRules(Main.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_OUTPUT); + } + + private void inspect(CodeInspector inspector) { + ClassSubject mainClassSubject = inspector.clazz(Main.class); + assertArrayGetCountEquals(1, mainClassSubject.uniqueMethodWithName("testRedundantArrayGet")); + assertArrayGetCountEquals( + 1, mainClassSubject.uniqueMethodWithName("testRedundantArrayGetAfterPutToUnrelatedArray")); + assertArrayGetCountEquals( + 1, mainClassSubject.uniqueMethodWithName("testRedundantArrayGetAfterPutToUnrelatedIndex")); + assertArrayGetCountEquals( + 2, mainClassSubject.uniqueMethodWithName("testNecessaryArrayGetAfterAliasedArrayPut")); + assertArrayGetCountEquals( + 2, mainClassSubject.uniqueMethodWithName("testNecessaryArrayGetAfterExternalSideEffect")); + } + + static void assertArrayGetCountEquals(int expected, MethodSubject methodSubject) { + assertEquals( + expected, + methodSubject.streamInstructions().filter(InstructionSubject::isArrayGet).count()); + } + + static class Main { + + public static void main(String[] args) { + int[] ints = new int[] {0, 1}; + long[] longs = new long[] {0}; + testRedundantArrayGet(ints, 0); + testRedundantArrayGetAfterPutToUnrelatedArray(ints, longs, 0); + testRedundantArrayGetAfterPutToUnrelatedIndex(ints, ints); + testNecessaryArrayGetAfterAliasedArrayPut(ints, ints, 0, 0); + testNecessaryArrayGetAfterExternalSideEffect(ints, 0, () -> ints[0] = 42); + } + + static void testRedundantArrayGet(int[] array, int index) { + int first = array[index]; + int second = array[index]; // Redundant. + System.out.println(first + second); + } + + static void testRedundantArrayGetAfterPutToUnrelatedArray( + int[] array, long[] otherArray, int index) { + int first = array[index]; + otherArray[index] = 42; + int second = array[index]; // Redundant. + System.out.println(first + second); + } + + static void testRedundantArrayGetAfterPutToUnrelatedIndex(int[] array, int[] sameArray) { + int first = array[0]; + sameArray[1] = 42; + int second = array[0]; // Redundant. + System.out.println(first + second); + } + + static void testNecessaryArrayGetAfterAliasedArrayPut( + int[] array, int[] sameArray, int index, int sameIndex) { + int first = array[index]; + sameArray[sameIndex] = 42; + int second = array[index]; // Not redundant. + System.out.println(first + second); + } + + static void testNecessaryArrayGetAfterExternalSideEffect( + int[] array, int index, Runnable runnable) { + int first = array[index]; + runnable.run(); + int second = array[index]; // Not redundant. + System.out.println(first + second); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java index 35b475b..c12a128 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java
@@ -9,6 +9,7 @@ import static org.junit.Assert.assertEquals; import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoMethodStaticizing; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -68,13 +69,19 @@ "Caught NullPointerException from testRewriteInvokeVirtualToThrowNull" + "WithDeadCatchHandler"); - testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expected); + if (parameters.isCfRuntime()) { + testForJvm() + .addTestClasspath() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(expected); + } CodeInspector inspector = testForR8(parameters.getBackend()) .addInnerClasses(InvokeMethodWithNonNullParamCheckTest.class) .addKeepMainRule(TestClass.class) .enableInliningAnnotations() + .enableNoMethodStaticizingAnnotations() .addOptionsModification( options -> { // Avoid that the class inliner inlines testRewriteInvokeVirtualToThrowNullWith- @@ -371,6 +378,7 @@ static class Virtual { @NeverInline + @NoMethodStaticizing public String throwIfFirstIsNull(Object first) { if (first == null) { throw new NullPointerException(); @@ -379,6 +387,7 @@ } @NeverInline + @NoMethodStaticizing public String throwIfSecondIsNull(Object first, Object second, Object third) { if (second == null) { throw new NullPointerException();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java index 7e12a9f..ad4f8a2 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -5,6 +5,7 @@ import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0; import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; @@ -248,11 +249,14 @@ KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName"); assertThat(familyName, not(isPresent())); + FieldSubject ageField = person.uniqueFieldWithName("age"); + assertThat(ageField, isAbsent()); + KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age"); assertThat(age, isPresent()); assertThat(age, not(isExtensionProperty())); - assertEquals(age.fieldSignature().asString(), "a:I"); - assertEquals(age.getterSignature().asString(), "getAge()I"); - assertEquals(age.setterSignature().asString(), "setAge(I)V"); + assertEquals("age:I", age.fieldSignature().asString()); + assertEquals("getAge()I", age.getterSignature().asString()); + assertEquals("setAge(I)V", age.setterSignature().asString()); } }
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java index 99554bf..5b13f6b 100644 --- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java +++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
@@ -98,7 +98,7 @@ public void runOnJvm() throws Throwable { Assume.assumeTrue(parameters.isCfRuntime()); testForJvm() - .addProgramClasses(CLASSPATH_CLASSES) + .addClasspathClasses(CLASSPATH_CLASSES) .addProgramClasses(PROGRAM_CLASSES) .run(parameters.getRuntime(), ProgramClass.class) .assertSuccessWithOutput(EXPECTED_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMissingInterfaceTest.java new file mode 100644 index 0000000..3eb80ae --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMissingInterfaceTest.java
@@ -0,0 +1,86 @@ +// 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.naming.applymapping; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.R8TestCompileResult; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class ApplyMappingMissingInterfaceTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void testR8() throws Exception { + R8TestCompileResult libraryCompileResult = + testForR8(parameters.getBackend()) + .addLibraryClasses(LibraryI.class) + .addDefaultRuntimeLibrary(parameters) + .addProgramClasses(ClassPathI.class) + .setMinApi(parameters.getApiLevel()) + .addKeepClassAndMembersRulesWithAllowObfuscation(ClassPathI.class) + .compile(); + + testForR8(parameters.getBackend()) + .addClasspathClasses(ClassPathI.class) + .addProgramClasses(ProgramInterface.class, Main.class) + .setMinApi(parameters.getApiLevel()) + .addApplyMapping(libraryCompileResult.getProguardMap()) + .addKeepMainRule(Main.class) + .enableInliningAnnotations() + .addDontWarn(LibraryI.class) + .compile() + .addRunClasspathFiles(libraryCompileResult.writeToZip()) + .addRunClasspathClasses(LibraryI.class) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("Main::foo", "Main::bar"); + } + + /* Will be missing on compile time */ + public interface LibraryI {} + + public interface ClassPathI extends LibraryI { + + void foo(); + } + + public interface ProgramInterface extends ClassPathI { + + void bar(); + } + + public static class Main implements ProgramInterface { + + public static void main(String[] args) { + new Main().foo(); + new Main().bar(); + } + + @Override + @NeverInline + public void foo() { + System.out.println("Main::foo"); + } + + @Override + @NeverInline + public void bar() { + System.out.println("Main::bar"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java index 554ea5c..01de767 100644 --- a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java +++ b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java
@@ -199,7 +199,8 @@ private R8Command.Builder getCommandForInstrumentation( Path out, Path flag, Path mainApp, Path instrApp) throws IOException { return R8Command.builder() - .addLibraryFiles(runtimeJar(backend), mainApp) + .addLibraryFiles(runtimeJar(backend)) + .addClasspathFiles(mainApp) .addProgramFiles(instrApp) .setOutput(out, outputMode(backend)) .addProguardConfigurationFiles(flag);
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java index 6b7b2d0..99531f1 100644 --- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java +++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.naming.retrace; import static com.android.tools.r8.naming.retrace.StackTrace.isSame; -import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForLineNumbers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; import static org.hamcrest.MatcherAssert.assertThat; @@ -13,12 +12,10 @@ import com.android.tools.r8.NoMethodStaticizing; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.util.List; -import java.util.Objects; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -70,31 +67,9 @@ (stackTrace, inspector) -> { ClassSubject callerClass = inspector.clazz(Caller.class); assertThat(callerClass, isPresent()); - MethodSubject staticized = callerClass.uniqueMethodWithName("outerCaller"); - assertThat(staticized, isPresentAndRenamed()); - // TODO(b/214377135): The stack traces should be the same (when 206427323) is - // resolved. - if (throwReceiverNpe && canUseJavaUtilObjectsRequireNonNull(parameters)) { - StackTrace requireNonNullFrame = - StackTrace.builder().add(stackTrace.get(0)).build(); - assertThat( - requireNonNullFrame, - isSameExceptForLineNumbers( - StackTrace.builder() - .add( - StackTraceLine.builder() - .setClassName(Objects.class.getTypeName()) - .setMethodName("requireNonNull") - .setFileName("Objects.java") - .build()) - .build())); - - StackTrace stackTraceWithoutRequireNonNull = - StackTrace.builder().add(stackTrace).remove(0).build(); - assertThat(stackTraceWithoutRequireNonNull, isSame(expectedStackTrace)); - } else { - assertThat(stackTrace, isSame(expectedStackTrace)); - } + MethodSubject outerCaller = callerClass.uniqueMethodWithName("outerCaller"); + assertThat(outerCaller, isPresentAndRenamed()); + assertThat(stackTrace, isSame(expectedStackTrace)); }); } @@ -119,7 +94,7 @@ static void caller(Foo f) { Object inlinable = f.inlinable(); - System.out.println(inlinable); + System.out.println(inlinable == null ? "null" : "some"); } @NeverInline
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java index bf8856b..be348ec 100644 --- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java +++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
@@ -8,7 +8,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import com.android.tools.r8.NeverInline; -import com.android.tools.r8.SingleTestRunResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine; @@ -37,17 +36,12 @@ @Before public void setup() throws Exception { // Get the expected stack trace by running on the JVM. - SingleTestRunResult<?> runResult = + expectedStackTrace = testForRuntime(parameters) .addProgramClasses(Caller.class, Foo.class) .run(parameters.getRuntime(), Caller.class) - .assertFailureWithErrorThatThrows(NullPointerException.class); - if (parameters.isCfRuntime()) { - expectedStackTrace = runResult.map(StackTrace::extractFromJvm); - } else { - expectedStackTrace = - StackTrace.extractFromArt(runResult.getStdErr(), parameters.asDexRuntime().getVm()); - } + .assertFailureWithErrorThatThrows(NullPointerException.class) + .getStackTrace(); } @Test
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java index 828a4b5..a728f8c 100644 --- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java +++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java
@@ -4,9 +4,9 @@ package com.android.tools.r8.optimize.argumentpropagation; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic; -import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf; import static junit.framework.TestCase.assertEquals; import static org.hamcrest.MatcherAssert.assertThat; @@ -16,7 +16,6 @@ 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.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; import org.junit.Test; @@ -48,19 +47,16 @@ .compile() .inspect( inspector -> { - boolean isCf = parameters.isCfRuntime(); - ClassSubject hostClassSubject = inspector.clazz(Host.class); assertThat(hostClassSubject, isPresent()); - assertEquals(1 + BooleanUtils.intValue(isCf), hostClassSubject.allMethods().size()); - assertThat(hostClassSubject.clinit(), onlyIf(isCf, isPresent())); + assertEquals(1, hostClassSubject.allMethods().size()); + assertThat(hostClassSubject.clinit(), isAbsent()); assertThat(hostClassSubject.uniqueMethodWithName("keepHost"), isPresent()); ClassSubject companionClassSubject = inspector.clazz(Host.Companion.class); assertThat(companionClassSubject, isPresent()); - assertEquals( - 1 + BooleanUtils.intValue(isCf), companionClassSubject.allMethods().size()); - assertThat(companionClassSubject.init(), onlyIf(isCf, isPresent())); + assertEquals(1, companionClassSubject.allMethods().size()); + assertThat(companionClassSubject.init(), isAbsent()); MethodSubject greetMethodSubject = companionClassSubject.uniqueMethodWithName("greet");
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java index 3bd714f..ab23d56 100644 --- a/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java +++ b/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java
@@ -30,6 +30,9 @@ public void testRuntime() throws Exception { // This tests shows that invoke super behaves differently on V5_1_1 and V6_0_1 based on the // static receiver on invoke-super, skipping the direct resolution target. + // https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.3 specify that + // the static target of an invoke-super should always be an immediate super type and the + // generated byte code is therefore invalid according to the spec. boolean hasIncorrectSuperInvokeSemantics = parameters.isDexRuntimeVersion(Version.V5_1_1) || parameters.isDexRuntimeVersion(Version.V6_0_1);
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java index b769752..7299dfb 100644 --- a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java +++ b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
@@ -944,6 +944,108 @@ }); } + /* This is a regression test for b/216359244 */ + @Test + public void testMissingSourceFile() { + runRetraceTest( + ".*FOO\\s+:\\s+\\[\\d+\\]\\s+%c\\.%m\\(%l\\):.*", + new StackTraceForTest() { + @Override + public List<String> obfuscatedStackTrace() { + return ImmutableList.of( + "12-24 19:53:19.052 10197 30302 30302 I FOO : [2] huk.g(1): getDownloads()"); + } + + @Override + public String mapping() { + return "foo.Bar -> huk:\n" + " void baz():13:13 -> g\n" + " void qux():12:12 -> g\n"; + } + + @Override + public List<String> retracedStackTrace() { + return ImmutableList.of( + "12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.baz(13): getDownloads()", + "<OR> 12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.qux(12):" + + " getDownloads()"); + } + + @Override + public List<String> retraceVerboseStackTrace() { + return ImmutableList.of( + "12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.void baz()(13):" + + " getDownloads()", + "<OR> 12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.void qux()(12):" + + " getDownloads()"); + } + + @Override + public int expectedWarnings() { + return 0; + } + }); + } + + @Test + public void testBigLine() { + runRetraceTest( + RetraceOptions.defaultRegularExpression(), + new StackTraceForTest() { + + private final String LINE = + "12-16 19:29:11.111 media 453 490 D WVCdm : [for_bar.cpp(334):AddKey] key_data =" + + " (1401)" + + " CAISvgYKmgIKIEIzMkU1QkU3RjlGMThCNzYzQjAwMDAwMDAwMDAwMDAwEuMBeyJ2ZXJzaW9uIjoiM" + + "S4wIiwiZXNuIjoiTkZBTkRST0lEMi1QUlYtQk9SRUFMLUdPT0dMQ0hST01FQ0FTVD1IRC0yMTc5My0" + + "1Nzk0RDMyRTQxRUI5QTc2QzZFQzIyNTdEQzVGNTQ2MTNDNzdBQTdCNzAwNzcyMkREQjVCRDYzRUFER" + + "TZBRUUyIiwic2FsdCI6IjQ5OTI3MDU0ODMyNzI0NjIxMDc0MDM4Nzg3MDkxMjAyNCIsImlzc3VlVGl" + + "tZSI6MTYzOTY2MzE1MDAwMCwibW92aWVJZCI6IjgxMTg0NDE3In0gASgAOMDRAkDA0QJIroztjQYSF" + + "AgBEAAYACDA0QIowNECWABgAXgBGmYSEBnHXFI_ttc_1lja-bpjqHkaUN_81aKusLLERz_xX0vPIR1" + + "I8yu9u9zQndpy1aEFJYtQiB0sed6wAC3c6aeH4oLsGFPiuVgGweP1MGc3yxunqusDpoGe03JGD1VSE" + + "9aB0jSKIAEahgEKEAAAAAAGmcHCAAAAAAAAAAASEN4b9_2XH75TnxdBkpJ4YhoaIOdhQxec7DpUs0D" + + "ixSQXTlvjbXqX3PIOg9DiQLCa4vbzIAIoBToECAEQKkI0CiDuiCOCIxvTlCqkYTKBWzMOfeh9BpKc5" + + "RDdE2omMbOfaRIQRWVqtbR8Qg4JEY0u0CAFRBqGAQoQAAAAAAaZwcMAAAAAAAAAABIQ7iP9449yr2F" + + "_97v4QHerRxognKGBtw2cwGpxKltxU7A_1asVTS0aPaw8dr5e2rP2SW4gAigFOgQIARAqQjQKIB-50" + + "Ou_RGn8rzRDr76FeWpBLYSt99zHVSZK0xnd_anREhC4eACukLOUwL5TQ8w1oOAVGoYBChAAAAAABpn" + + "BwQAAAAAAAAAAEhDsGE2bR6VgRM85PY3YU9qIGiAhD3h7I8Cv5Irr315yecWo0YA1t9_sr0g2z0zsa" + + "6Dq0yACKAE6BAgBECpCNAogQoIDcKUGMhCA-HetsARASXxXJUazkIIaSwCKE9asAEwSEMW4TIMZZkc" + + "r3LYIITelMlIgroztjQY4ABogzw-SGqQ1AYy8EopI2zSvXX_hkFpOoWxCvFQuQEj5oQwigAIRzNhvB" + + "9-YMOy2Muentb574WJKnVqXfEor5mFDIQ3vDELdjbkibaS4THyK2DlUQyp0M3C8n5bQ1JApslsfh5w" + + "SEMvA8Y0sXE9H6xnAKnMJTioZJOTElqPrbbRM5APhI3ohL8rp7u8ydBm9aWqkprDngrU3b1KdHd6J9" + + "YiJRJ4Y55M4qqCdwbCqr57xYAu_IIH_p8erfnhfNgUE2svtZBiLMP37vOMSQAP08_zkMf87VRHBxuI" + + "2rlnGcdgLoJyPgLp2ZBbdLw-b4Nq6uIZQfNABQ1SuM3OZ6uD6eq-NH9FawWvN0EmzWZ88DGiHtgM7h" + + "zqvIb8JNmLInt28us-vw6KQOggKBjE2LjUuMEABSoACAAAAAgAAAQAABAAQZrvQTwAAABMAAAE3AAA" + + "AEAAAAUkAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAKjAA" + + "AAAAAAAqMAAAAAAAAAAAAAAAAMAAAGgAAAAEAAAAbIAAAAQAAABxAAAABAAAAIUAAAAEAAAAfIAAAA" + + "gAAACKQAAABAAAAI7AAAAEAAAAk0AAAAQAAACnQAAABAAAAJ7AAAAIAAAArIAAAAQAAACxAAAABAAA" + + "ALWAAAAEAAAAyYAAAAQAAADBAAAACC8nuR7W9i8AuG9xF3K__n1kk9F_G1i25QRKR9YXyssdsadWT"; + + @Override + public List<String> obfuscatedStackTrace() { + return ImmutableList.of(LINE); + } + + @Override + public String mapping() { + return "foo.Bar -> huk:\n" + " void baz():13:13 -> g\n" + " void qux():12:12 -> g\n"; + } + + @Override + public List<String> retracedStackTrace() { + return ImmutableList.of(LINE); + } + + @Override + public List<String> retraceVerboseStackTrace() { + return ImmutableList.of(LINE); + } + + @Override + public int expectedWarnings() { + return 0; + } + }); + } + private TestDiagnosticMessagesImpl runRetraceTest( String regularExpression, StackTraceForTest stackTraceForTest) { TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java index 29960f9..1c7492d 100644 --- a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java +++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
@@ -7,8 +7,6 @@ import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; @@ -59,13 +57,9 @@ assertThat(iClassSubject, isPresent()); assertThat(iClassSubject.clinit(), isPresent()); - // Verify that J is still there. - assertThat(jClassSubject, isPresent()); - - // Verify that A still implements J. - assertThat(aClassSubject, isPresent()); - assertEquals(1, aClassSubject.getDexProgramClass().getInterfaces().size()); - assertTrue(aClassSubject.isImplementing(jClassSubject)); + // Verify that J and A are pruned. + assertThat(jClassSubject, isAbsent()); + assertThat(aClassSubject, isAbsent()); } else { // All interfaces are gone and the default methods companion call is inlined. assertThat(iClassSubject, isAbsent());
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java index 035ddf7..c238e5d 100644 --- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java +++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoMethodStaticizing; import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; @@ -27,6 +28,7 @@ public static class A { @NeverInline + @NoMethodStaticizing void foo() { System.out.println("A.foo!"); } @@ -75,6 +77,7 @@ testForR8(parameters.getBackend()) .enableGraphInspector() .enableNeverClassInliningAnnotations() + .enableNoMethodStaticizingAnnotations() .enableNoVerticalClassMergingAnnotations() .enableInliningAnnotations() .addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java index 4465113..40bfdec 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.cf.code.CfArithmeticBinop; import com.android.tools.r8.cf.code.CfArrayLength; +import com.android.tools.r8.cf.code.CfArrayLoad; import com.android.tools.r8.cf.code.CfArrayStore; import com.android.tools.r8.cf.code.CfCheckCast; import com.android.tools.r8.cf.code.CfConstClass; @@ -368,6 +369,11 @@ } @Override + public boolean isArrayGet() { + return instruction instanceof CfArrayLoad; + } + + @Override public boolean isArrayPut() { return instruction instanceof CfArrayStore; }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java index 91f0ea9..00cfc92 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -4,6 +4,13 @@ package com.android.tools.r8.utils.codeinspector; +import com.android.tools.r8.code.Aget; +import com.android.tools.r8.code.AgetBoolean; +import com.android.tools.r8.code.AgetByte; +import com.android.tools.r8.code.AgetChar; +import com.android.tools.r8.code.AgetObject; +import com.android.tools.r8.code.AgetShort; +import com.android.tools.r8.code.AgetWide; import com.android.tools.r8.code.Aput; import com.android.tools.r8.code.AputBoolean; import com.android.tools.r8.code.AputByte; @@ -458,6 +465,17 @@ } @Override + public boolean isArrayGet() { + return instruction instanceof Aget + || instruction instanceof AgetBoolean + || instruction instanceof AgetByte + || instruction instanceof AgetChar + || instruction instanceof AgetObject + || instruction instanceof AgetShort + || instruction instanceof AgetWide; + } + + @Override public boolean isArrayPut() { return instruction instanceof Aput || instruction instanceof AputBoolean
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java index 9f55b56..4488d83 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -144,6 +144,8 @@ boolean isArrayLength(); + boolean isArrayGet(); + boolean isArrayPut(); boolean isMonitorEnter();
diff --git a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 index ffee6ee..c55bcfe 100644 --- a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 +++ b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
@@ -1 +1 @@ -36741d08e769bd2c5c201698161b3407507c7535 \ No newline at end of file +bf40dc3b9e57b7759d8da51fb70d9995af1a188d \ No newline at end of file
diff --git a/tools/retrace.py b/tools/retrace.py index 14593ab..22753fb 100755 --- a/tools/retrace.py +++ b/tools/retrace.py
@@ -61,6 +61,11 @@ default=None, action='store_true', help='Enables verbose retracing.') + parser.add_argument( + '--disable-map-validation', + default=None, + action='store_true', + help='Disable validation of map hash.') return parser.parse_args() @@ -108,7 +113,7 @@ print(e) print('WARNING: Falling back to using local mapping file.') - if map_path: + if map_path and not args.disable_map_validation: check_maphash(map_path, maphash) return map_path