Merge commit 'a5cd84a21f27683a45068a86549f441586c922a6' into dev-release Change-Id: I30e45ebb967331f6c5e64cc83ece3cd760284472
diff --git a/build.gradle b/build.gradle index 7f2f61d..f7b3a44 100644 --- a/build.gradle +++ b/build.gradle
@@ -1709,6 +1709,9 @@ if (project.hasProperty('command_cache_dir')) { systemProperty 'command_cache_dir', project.property('command_cache_dir') } + if (project.hasProperty('command_cache_stats_dir')) { + systemProperty 'command_cache_stats_dir', project.property('command_cache_stats_dir') + } if (project.hasProperty('r8lib')) { dependsOn configureTestForR8Lib // R8lib should be used instead of the main output and all the tests in
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt index c0412fa..e2d1fd8 100644 --- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt +++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -207,18 +207,35 @@ } fun Project.getJavaHome(jdk : Jdk) : File { - // TODO(b/270105162): Make sure this works on other platforms. - return getRoot().resolveAll("third_party", "openjdk", jdk.folder, "linux") + val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem() + var osFolder = "linux" + if (os.isWindows()) { + osFolder = "windows" + } + if (os.isMacOsX) { + osFolder = "osx" + } + return getRoot().resolveAll("third_party", "openjdk", jdk.folder, osFolder) } fun Project.getCompilerPath(jdk : Jdk) : String { - // TODO(b/270105162): Make sure this works on other platforms. - return getJavaHome(jdk).resolveAll("bin", "javac").toString() + val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem() + val binary = if (os.isWindows()) "javac.exe" else "javac" + return getJavaHome(jdk).resolveAll("bin", binary).toString() } fun Project.getJavaPath(jdk : Jdk) : String { - // TODO(b/270105162): Make sure this works on other platforms. - return getJavaHome(jdk).resolveAll("bin", "java").toString() + val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem() + val binary = if (os.isWindows()) "java.exe" else "java" + return getJavaHome(jdk).resolveAll("bin", binary).toString() +} + +fun Project.getClasspath(vararg paths: File) : String { + val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem() + assert (!paths.isEmpty()) + val separator = if (os.isWindows()) ";" else ":" + var classpath = paths.joinToString(separator = separator) { it -> it.toString() } + return classpath } fun Project.baseCompilerCommandLine( @@ -231,7 +248,7 @@ "-Xmx8g", "-ea", "-cp", - "$jar:$deps", + getClasspath(jar, deps), "com.android.tools.r8.SwissArmyKnife", compiler) + args } @@ -349,6 +366,11 @@ Paths.get("third_party", "chrome", "clank_google3_prebuilt").toFile(), Paths.get("third_party", "chrome", "clank_google3_prebuilt.tar.gz.sha1").toFile(), DependencyType.X20) + val chrome = ThirdPartyDependency( + "chrome", + Paths.get("third_party", "chrome", "chrome_200430").toFile(), + Paths.get("third_party", "chrome", "chrome_200430.tar.gz.sha1").toFile(), + DependencyType.X20) val compilerApi = ThirdPartyDependency( "compiler-api", Paths.get( @@ -420,6 +442,10 @@ "google-java-format", Paths.get("third_party", "google-java-format").toFile(), Paths.get("third_party", "google-java-format.tar.gz.sha1").toFile()) + val googleJavaFormat_1_14 = ThirdPartyDependency( + "google-java-format-1.14", + Paths.get("third_party", "google", "google-java-format", "1.14.0").toFile(), + Paths.get("third_party", "google", "google-java-format", "1.14.0.tar.gz.sha1").toFile()) val gson = ThirdPartyDependency( "gson", Paths.get("third_party", "gson", "gson-2.10.1").toFile(), @@ -468,6 +494,10 @@ "kotlinR8TestResources", Paths.get("third_party", "kotlinR8TestResources").toFile(), Paths.get("third_party", "kotlinR8TestResources.tar.gz.sha1").toFile()) + val kotlinxCoroutines = ThirdPartyDependency( + "kotlinx-coroutines-1.3.6", + Paths.get("third_party", "kotlinx-coroutines-1.3.6").toFile(), + Paths.get("third_party", "kotlinx-coroutines-1.3.6.tar.gz.sha1").toFile()) val multidex = ThirdPartyDependency( "multidex", Paths.get("third_party", "multidex").toFile(),
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt b/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt index 9a8c17f..943feea 100644 --- a/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt +++ b/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt
@@ -90,7 +90,8 @@ "desugar_jdk_json_dir", "desugar_jdk_libs", "test_dir", - "command_cache_dir").forEach { + "command_cache_dir", + "command_cache_stats_dir").forEach { val propertyName = it if (project.hasProperty(propertyName)) { project.property(propertyName)?.let { v -> test.systemProperty(propertyName, v) }
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java index 4a8b427..a5fe760 100644 --- a/src/main/java/com/android/tools/r8/graph/AppView.java +++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -343,6 +343,10 @@ return abstractValueParameterJoiner; } + public void clearMethodResolutionOptimizationInfoCollection() { + methodResolutionOptimizationInfoCollection = MethodResolutionOptimizationInfoCollection.empty(); + } + public MethodResolutionOptimizationInfoCollection getMethodResolutionOptimizationInfoCollection() { return methodResolutionOptimizationInfoCollection;
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java index d4db53d..87740a8 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -621,6 +621,12 @@ return accessFlags.isSynthetic(); } + /** Returns true if this method is synthetic and a bridge method. */ + public boolean isSyntheticBridgeMethod() { + checkIfObsolete(); + return accessFlags.isSynthetic() && accessFlags.isBridge(); + } + public boolean belongsToDirectPool() { return accessFlags.belongsToDirectPool(); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java index 0d5f834..38b5634 100644 --- a/src/main/java/com/android/tools/r8/graph/DexValue.java +++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -460,11 +460,6 @@ public DexValueNumber asDexValueNumber() { return this; } - - @Override - public AbstractValue toAbstractValue(AbstractValueFactory factory) { - return factory.createSingleNumberValue(getRawValue()); - } } public static class DexValueByte extends DexValueNumber { @@ -537,6 +532,11 @@ } @Override + public AbstractValue toAbstractValue(AbstractValueFactory factory) { + return factory.createSingleNumberValue(getRawValue(), TypeElement.getByte()); + } + + @Override public int hashCode() { return value * 3; } @@ -629,6 +629,11 @@ } @Override + public AbstractValue toAbstractValue(AbstractValueFactory factory) { + return factory.createSingleNumberValue(getRawValue(), TypeElement.getShort()); + } + + @Override public int hashCode() { return value * 7; } @@ -711,6 +716,11 @@ } @Override + public AbstractValue toAbstractValue(AbstractValueFactory factory) { + return factory.createSingleNumberValue(getRawValue(), TypeElement.getChar()); + } + + @Override public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { dest.forward(1); int length = dest.putUnsignedEncodedValue(value, 2); @@ -817,6 +827,11 @@ } @Override + public AbstractValue toAbstractValue(AbstractValueFactory factory) { + return factory.createSingleNumberValue(getRawValue(), TypeElement.getInt()); + } + + @Override public int hashCode() { return value * 11; } @@ -909,6 +924,11 @@ } @Override + public AbstractValue toAbstractValue(AbstractValueFactory factory) { + return factory.createSingleNumberValue(getRawValue(), TypeElement.getLong()); + } + + @Override public int hashCode() { return (int) value * 13; } @@ -1011,6 +1031,11 @@ } @Override + public AbstractValue toAbstractValue(AbstractValueFactory factory) { + return factory.createSingleNumberValue(getRawValue(), TypeElement.getFloat()); + } + + @Override public int hashCode() { return (int) (value * 19); } @@ -1109,6 +1134,11 @@ } @Override + public AbstractValue toAbstractValue(AbstractValueFactory factory) { + return factory.createSingleNumberValue(getRawValue(), TypeElement.getDouble()); + } + + @Override public int hashCode() { return (int) (value * 29); } @@ -1853,6 +1883,11 @@ } @Override + public AbstractValue toAbstractValue(AbstractValueFactory factory) { + return factory.createUncheckedNullValue(); + } + + @Override public int hashCode() { return 42; } @@ -1949,6 +1984,11 @@ } @Override + public AbstractValue toAbstractValue(AbstractValueFactory factory) { + return factory.createSingleNumberValue(getRawValue(), TypeElement.getBoolean()); + } + + @Override public int hashCode() { return value ? 1234 : 4321; }
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java index 6ef6ae4..53e4ddc 100644 --- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java +++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -116,7 +116,7 @@ getDefinition().setApiLevelForCode(appView.computedMinApiLevel()); setCode(ThrowNullCode.get(), appView); getSimpleFeedback().markProcessed(getDefinition(), ConstraintWithTarget.ALWAYS); - getSimpleFeedback().unsetOptimizationInfoForThrowNullMethod(this); + getSimpleFeedback().unsetOptimizationInfoForThrowNullMethod(appView, this); } public void registerCodeReferences(UseRegistry<?> registry) {
diff --git a/src/main/java/com/android/tools/r8/graph/proto/RemovedArgumentInfo.java b/src/main/java/com/android/tools/r8/graph/proto/RemovedArgumentInfo.java index 839756a..8d767ad 100644 --- a/src/main/java/com/android/tools/r8/graph/proto/RemovedArgumentInfo.java +++ b/src/main/java/com/android/tools/r8/graph/proto/RemovedArgumentInfo.java
@@ -88,9 +88,11 @@ @SuppressWarnings("ReferenceEquality") public RemovedArgumentInfo rewrittenWithLens( AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) { - SingleValue rewrittenSingleValue = - hasSingleValue() ? singleValue.rewrittenWithLens(appView, graphLens, codeLens) : null; DexType rewrittenType = graphLens.lookupType(type, codeLens); + SingleValue rewrittenSingleValue = + hasSingleValue() + ? singleValue.rewrittenWithLens(appView, rewrittenType, graphLens, codeLens) + : null; if (rewrittenSingleValue != singleValue || rewrittenType != type) { return new RemovedArgumentInfo(rewrittenSingleValue, rewrittenType); }
diff --git a/src/main/java/com/android/tools/r8/graph/proto/RemovedReceiverInfo.java b/src/main/java/com/android/tools/r8/graph/proto/RemovedReceiverInfo.java index 7a1391c..ee7aa41 100644 --- a/src/main/java/com/android/tools/r8/graph/proto/RemovedReceiverInfo.java +++ b/src/main/java/com/android/tools/r8/graph/proto/RemovedReceiverInfo.java
@@ -26,9 +26,11 @@ @SuppressWarnings("ReferenceEquality") public RemovedReceiverInfo rewrittenWithLens( AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) { - SingleValue rewrittenSingleValue = - hasSingleValue() ? getSingleValue().rewrittenWithLens(appView, graphLens, codeLens) : null; DexType rewrittenType = graphLens.lookupType(getType(), codeLens); + SingleValue rewrittenSingleValue = + hasSingleValue() + ? getSingleValue().rewrittenWithLens(appView, rewrittenType, graphLens, codeLens) + : null; if (rewrittenSingleValue != getSingleValue() || rewrittenType != getType()) { return new RemovedReceiverInfo(rewrittenSingleValue, rewrittenType); }
diff --git a/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java index cde2daf..af66b41 100644 --- a/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java +++ b/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java
@@ -175,7 +175,7 @@ SingleValue rewrittenSingleValue = rewrittenReturnInfo .getSingleValue() - .rewrittenWithLens(appView, appView.graphLens(), codeLens); + .rewrittenWithLens(appView, method.getReturnType(), appView.graphLens(), codeLens); assert rewrittenSingleValue.isMaterializableInContext(appView, method); return true; }
diff --git a/src/main/java/com/android/tools/r8/graph/proto/RewrittenTypeInfo.java b/src/main/java/com/android/tools/r8/graph/proto/RewrittenTypeInfo.java index 8537d1d..fb10019 100644 --- a/src/main/java/com/android/tools/r8/graph/proto/RewrittenTypeInfo.java +++ b/src/main/java/com/android/tools/r8/graph/proto/RewrittenTypeInfo.java
@@ -97,7 +97,9 @@ DexType rewrittenCastType = castType != null ? graphLens.lookupType(castType, codeLens) : null; DexType rewrittenNewType = graphLens.lookupType(newType, codeLens); SingleValue rewrittenSingleValue = - hasSingleValue() ? getSingleValue().rewrittenWithLens(appView, graphLens, codeLens) : null; + hasSingleValue() + ? getSingleValue().rewrittenWithLens(appView, rewrittenNewType, graphLens, codeLens) + : null; if (rewrittenCastType != castType || rewrittenNewType != newType || rewrittenSingleValue != singleValue) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java index 507396f..06117ec 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -134,7 +134,10 @@ newMethodReference.withName("$r8$clinit$synthetic", dexItemFactory); lensBuilder.recordNewMethodSignature(syntheticMethodReference, newMethodReference, true); - ComputedApiLevel apiReferenceLevel = classInitializerMerger.getApiReferenceLevel(appView); + ComputedApiLevel apiReferenceLevel = + appView.options().apiModelingOptions().isApiLibraryModelingEnabled() + ? classInitializerMerger.getApiReferenceLevel(appView) + : ComputedApiLevel.notSet(); DexEncodedMethod definition = DexEncodedMethod.syntheticBuilder() .setMethod(newMethodReference)
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java index 15f0d57..a77873f 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
@@ -254,17 +254,16 @@ new CfDexItemBasedConstString( dexItemBasedStringValue.getItem(), dexItemBasedStringValue.getNameComputationInfo())); return 1; + } else if (singleConstValue.isNull()) { + assert type.isReferenceType(); + instructionBuilder.add(new CfConstNull()); + return 1; } else if (singleConstValue.isSingleNumberValue()) { - if (type.isReferenceType()) { - assert singleConstValue.isNull(); - instructionBuilder.add(new CfConstNull()); - return 1; - } else { - instructionBuilder.add( - new CfConstNumber( - singleConstValue.asSingleNumberValue().getValue(), ValueType.fromDexType(type))); - return type.getRequiredRegisters(); - } + assert type.isPrimitiveType(); + instructionBuilder.add( + new CfConstNumber( + singleConstValue.asSingleNumberValue().getValue(), ValueType.fromDexType(type))); + return type.getRequiredRegisters(); } else { assert singleConstValue.isSingleStringValue(); instructionBuilder.add(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java index 583d6be..29db86f 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
@@ -21,6 +21,7 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.analysis.value.AbstractValueFactory; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.InstancePut; @@ -193,9 +194,13 @@ .createArgumentInitializationInfo(root.getDefinition().asArgument().getIndex()); } if (definition.isConstNumber()) { - return appView - .abstractValueFactory() - .createSingleNumberValue(definition.asConstNumber().getRawValue()); + AbstractValueFactory abstractValueFactory = appView.abstractValueFactory(); + if (definition.getOutType().isReferenceType()) { + return abstractValueFactory.createNullValue(definition.getOutType()); + } else { + return abstractValueFactory.createSingleNumberValue( + definition.asConstNumber().getRawValue(), definition.getOutType()); + } } if (definition.isConstString()) { return appView
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java index 49ecbf0..712fa0d 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -197,7 +197,9 @@ // value for fields that are not definitely null, we need to prove that the given field is never // read before it is written. AbstractValue abstractValue = - value.isZero() ? abstractValueFactory.createZeroValue() : AbstractValue.unknown(); + value.isZero() + ? abstractValueFactory.createDefaultValue(field.getType()) + : AbstractValue.unknown(); fieldStates.compute( field.getDefinition(), (f, fieldState) -> { @@ -328,7 +330,8 @@ private void recordAllFieldPutsProcessed( ProgramField field, OptimizationFeedbackDelayed feedback) { FieldState fieldState = fieldStates.getOrDefault(field.getDefinition(), FieldState.bottom()); - AbstractValue abstractValue = fieldState.getAbstractValue(appView.abstractValueFactory()); + AbstractValue abstractValue = + fieldState.getAbstractValue(appView.abstractValueFactory(), field); if (abstractValue.isNonTrivial()) { feedback.recordFieldHasAbstractValue(field.getDefinition(), appView, abstractValue); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java index 5f8e465..2161735 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -119,7 +119,7 @@ markFieldAsDead(field); getSimpleFeedback() .recordFieldHasAbstractValue( - field, appView, appView.abstractValueFactory().createNullValue()); + field, appView, appView.abstractValueFactory().createDefaultValue(field.getType())); } private void markFieldAsDead(DexEncodedField field) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/BottomFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/BottomFieldState.java index c3474af..8a67c43 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/BottomFieldState.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/BottomFieldState.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.analysis.fieldaccess.state; +import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.analysis.value.AbstractValueFactory; @@ -19,8 +20,9 @@ } @Override - public AbstractValue getAbstractValue(AbstractValueFactory abstractValueFactory) { - return abstractValueFactory.createNullValue(); + public AbstractValue getAbstractValue( + AbstractValueFactory abstractValueFactory, ProgramField field) { + return abstractValueFactory.createDefaultValue(field.getType()); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcretePrimitiveTypeFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcretePrimitiveTypeFieldState.java index 1f329f5..d8759cf 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcretePrimitiveTypeFieldState.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcretePrimitiveTypeFieldState.java
@@ -26,7 +26,8 @@ } @Override - public AbstractValue getAbstractValue(AbstractValueFactory abstractValueFactory) { + public AbstractValue getAbstractValue( + AbstractValueFactory abstractValueFactory, ProgramField field) { return abstractValue; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteReferenceTypeFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteReferenceTypeFieldState.java index 64d0674..4021c55 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteReferenceTypeFieldState.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteReferenceTypeFieldState.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.analysis.fieldaccess.state; +import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.analysis.value.AbstractValueFactory; @@ -17,7 +18,8 @@ } @Override - public AbstractValue getAbstractValue(AbstractValueFactory abstractValueFactory) { + public AbstractValue getAbstractValue( + AbstractValueFactory abstractValueFactory, ProgramField field) { return abstractValue; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/FieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/FieldState.java index 6e0b203..bcf3716 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/FieldState.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/FieldState.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.analysis.fieldaccess.state; +import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.analysis.value.AbstractValueFactory; @@ -18,7 +19,8 @@ return UnknownFieldState.getInstance(); } - public abstract AbstractValue getAbstractValue(AbstractValueFactory abstractValueFactory); + public abstract AbstractValue getAbstractValue( + AbstractValueFactory abstractValueFactory, ProgramField field); public boolean isArray() { return false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/UnknownFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/UnknownFieldState.java index e408f09..bf65dea 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/UnknownFieldState.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/UnknownFieldState.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.analysis.fieldaccess.state; +import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.analysis.value.AbstractValueFactory; @@ -19,7 +20,8 @@ } @Override - public AbstractValue getAbstractValue(AbstractValueFactory abstractValueFactory) { + public AbstractValue getAbstractValue( + AbstractValueFactory abstractValueFactory, ProgramField field) { return AbstractValue.unknown(); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java index e8c03c3..468274c 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -99,16 +99,8 @@ } AbstractValueFactory factory = appView.abstractValueFactory(); - if (value.isDexValueNumber()) { - feedback.recordFieldHasAbstractValue( - field, - appView, - factory.createSingleNumberValue(value.asDexValueNumber().getRawValue())); - } else if (value.isDexValueString()) { - feedback.recordFieldHasAbstractValue( - field, - appView, - factory.createSingleStringValue(value.asDexValueString().getValue())); + if (value.isDexValueNumber() || value.isDexValueString()) { + feedback.recordFieldHasAbstractValue(field, appView, value.toAbstractValue(factory)); } else if (value.isDexItemBasedValueString()) { // TODO(b/150835624): Extend to dex item based const strings. } else {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java index f84fb16..a831c7e 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState; import com.android.tools.r8.shaking.AppInfoWithLiveness; @@ -38,8 +39,8 @@ return false; } - public final boolean isNull() { - return isFalse(); + public boolean isNull() { + return false; } public final boolean isZero() { @@ -197,7 +198,7 @@ } public abstract AbstractValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens); + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens); @Override public abstract boolean equals(Object o);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java index 7b87326..cdd91ef 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.analysis.value.objectstate.KnownLengthArrayState; import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState; import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo; @@ -25,12 +26,17 @@ private ConcurrentHashMap<Integer, KnownLengthArrayState> knownArrayLengthStates = new ConcurrentHashMap<>(); + public SingleConstValue createDefaultValue(DexType type) { + assert type.isPrimitiveType() || type.isReferenceType(); + return type.isPrimitiveType() ? createZeroValue() : createNullValue(type); + } + public AbstractValue createDefiniteBitsNumberValue( int definitelySetBits, int definitelyUnsetBits) { if (definitelySetBits != 0 || definitelyUnsetBits != 0) { // If all bits are known, then create a single number value. if ((definitelySetBits | definitelyUnsetBits) == ALL_BITS_SET_MASK) { - return createSingleNumberValue(definitelySetBits); + return createUncheckedSingleNumberValue(definitelySetBits); } return new DefiniteBitsNumberValue(definitelySetBits, definitelyUnsetBits); } @@ -56,16 +62,31 @@ : new SingleStatefulFieldValue(field, state); } - public SingleNumberValue createSingleNumberValue(long value) { + public SingleNumberValue createSingleNumberValue(long value, TypeElement type) { + assert type.isPrimitiveType(); + return createUncheckedSingleNumberValue(value); + } + + public SingleNumberValue createUncheckedSingleNumberValue(long value) { return singleNumberValues.computeIfAbsent(value, SingleNumberValue::new); } - public SingleNumberValue createNullValue() { - return createSingleNumberValue(0); + public SingleNullValue createNullValue(DexType type) { + assert type.isReferenceType() : type; + return createUncheckedNullValue(); + } + + public SingleNullValue createNullValue(TypeElement type) { + assert type.isReferenceType(); + return createUncheckedNullValue(); + } + + public SingleNullValue createUncheckedNullValue() { + return SingleNullValue.get(); } public SingleNumberValue createZeroValue() { - return createSingleNumberValue(0); + return createUncheckedSingleNumberValue(0); } public SingleStringValue createSingleStringValue(DexString string) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java index 0653a7c..f5875b0 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.analysis.value; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; @@ -25,7 +26,7 @@ @Override public AbstractValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { return this; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/DefiniteBitsNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/DefiniteBitsNumberValue.java index 83ea1a0..778e61b 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/DefiniteBitsNumberValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/DefiniteBitsNumberValue.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.analysis.value; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.OptionalBool; @@ -114,7 +115,7 @@ @Override public AbstractValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { return this; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NullOrAbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NullOrAbstractValue.java index 875943f..6eaf130 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/NullOrAbstractValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NullOrAbstractValue.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.analysis.value; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; @@ -44,8 +45,8 @@ @Override public NullOrAbstractValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { - return new NullOrAbstractValue(value.rewrittenWithLens(appView, lens, codeLens)); + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { + return new NullOrAbstractValue(value.rewrittenWithLens(appView, newType, lens, codeLens)); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java index 9581c42..161f1a1 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.analysis.value; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.OptionalBool; @@ -87,7 +88,7 @@ @Override public AbstractValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { return this; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java index 91b53e1..6678c17 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.analysis.value; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.ArrayUtils; @@ -103,7 +104,7 @@ @Override public AbstractValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { return this; } @@ -170,7 +171,8 @@ assert !numbers.isEmpty(); assert numbers.size() <= MAX_SIZE; if (numbers.size() == 1) { - return abstractValueFactory.createSingleNumberValue(numbers.iterator().nextInt()); + return abstractValueFactory.createUncheckedSingleNumberValue( + numbers.iterator().nextInt()); } return new NumberFromSetValue(numbers); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java index d5047ac..ebe0e66 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
@@ -128,7 +128,7 @@ @Override @SuppressWarnings("ReferenceEquality") public SingleValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { assert lens.lookupType(type, codeLens) == type; return this; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java index c5f258a..e75ea41 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DebugLocalInfo; 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.lens.GraphLens; import com.android.tools.r8.graph.proto.ArgumentInfoCollection; @@ -116,7 +117,7 @@ @Override public SingleValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { return appView .abstractValueFactory() .createSingleDexItemBasedStringValue(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java index 52f0af1..05d232e 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -144,11 +144,12 @@ @Override @SuppressWarnings("ReferenceEquality") public SingleValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { AbstractValueFactory factory = appView.abstractValueFactory(); EnumDataMap enumDataMap = appView.unboxedEnums(); if (enumDataMap.hasUnboxedValueFor(field)) { - return factory.createSingleNumberValue(enumDataMap.getUnboxedValue(field)); + return factory.createSingleNumberValue( + enumDataMap.getUnboxedValue(field), TypeElement.getInt()); } DexField rewrittenField = lens.lookupField(field, codeLens); ObjectState rewrittenObjectState = getObjectState().rewrittenWithLens(appView, lens, codeLens);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNullValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNullValue.java new file mode 100644 index 0000000..eb49ec6 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNullValue.java
@@ -0,0 +1,91 @@ +// Copyright (c) 2023, 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.analysis.value; + +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DebugLocalInfo; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.lens.GraphLens; +import com.android.tools.r8.graph.proto.ArgumentInfoCollection; +import com.android.tools.r8.ir.analysis.type.TypeElement; +import com.android.tools.r8.ir.code.ConstNumber; +import com.android.tools.r8.ir.code.Instruction; +import com.android.tools.r8.ir.code.NumberGenerator; +import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier; +import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo; +import com.android.tools.r8.shaking.AppInfoWithLiveness; + +public class SingleNullValue extends SingleConstValue { + + private static final SingleNullValue INSTANCE = new SingleNullValue(); + + /** Intentionally package private, use {@link AbstractValueFactory} instead. */ + SingleNullValue() {} + + static SingleNullValue get() { + return INSTANCE; + } + + @Override + public boolean isNull() { + return true; + } + + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + public String toString() { + return "SingleNullValue"; + } + + @Override + public Instruction createMaterializingInstruction( + AppView<?> appView, + ProgramMethod context, + NumberGenerator valueNumberGenerator, + TypeAndLocalInfoSupplier info) { + assert info.getOutType().isReferenceType() : info.getOutType(); + DebugLocalInfo localInfo = info.getLocalInfo(); + Value returnedValue = new Value(valueNumberGenerator.next(), TypeElement.getNull(), localInfo); + return new ConstNumber(returnedValue, 0); + } + + @Override + boolean internalIsMaterializableInContext( + AppView<? extends AppInfoWithClassHierarchy> appView, ProgramMethod context) { + return true; + } + + @Override + public boolean isMaterializableInAllContexts(AppView<AppInfoWithLiveness> appView) { + return true; + } + + @Override + public InstanceFieldInitializationInfo fixupAfterParametersChanged( + ArgumentInfoCollection argumentInfoCollection) { + return this; + } + + @Override + public SingleValue rewrittenWithLens( + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { + if (newType.isIntType()) { + // Enum unboxing. + return appView.abstractValueFactory().createZeroValue(); + } + return this; + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java index 0708da5..c76a0db 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DebugLocalInfo; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.graph.proto.ArgumentInfoCollection; @@ -154,14 +155,10 @@ ProgramMethod context, NumberGenerator valueNumberGenerator, TypeAndLocalInfoSupplier info) { - TypeElement typeLattice = info.getOutType(); - DebugLocalInfo debugLocalInfo = info.getLocalInfo(); - assert !typeLattice.isReferenceType() || value == 0; - Value returnedValue = - new Value( - valueNumberGenerator.next(), - typeLattice.isReferenceType() ? TypeElement.getNull() : typeLattice, - debugLocalInfo); + TypeElement type = info.getOutType(); + assert type.isPrimitiveType() : type; + DebugLocalInfo localInfo = info.getLocalInfo(); + Value returnedValue = new Value(valueNumberGenerator.next(), type, localInfo); return new ConstNumber(returnedValue, value); } @@ -184,7 +181,7 @@ @Override public SingleValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { return this; } }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java index 78e547c..ff503f2 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DebugLocalInfo; import com.android.tools.r8.graph.DexString; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.graph.proto.ArgumentInfoCollection; @@ -102,7 +103,7 @@ @Override public SingleValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { return this; } }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java index 51bc690..ba1dab9 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.ir.code.IRCode; @@ -63,5 +64,5 @@ @Override public abstract SingleValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens); + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/StatefulObjectValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/StatefulObjectValue.java index 25deb94..e735de5 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/StatefulObjectValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/StatefulObjectValue.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.analysis.value; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState; import com.android.tools.r8.shaking.AppInfoWithLiveness; @@ -51,7 +52,7 @@ @Override public AbstractValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { return create(getObjectState().rewrittenWithLens(appView, lens, codeLens)); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java index e2ef4e0..990afee 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.analysis.value; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; @@ -30,7 +31,7 @@ @Override public AbstractValue rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { return this; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java index e5f5b5e..3b73b9d 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java
@@ -46,10 +46,12 @@ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { Map<DexField, AbstractValue> rewrittenState = new IdentityHashMap<>(); state.forEach( - (field, value) -> - rewrittenState.put( - lens.lookupField(field, codeLens), - value.rewrittenWithLens(appView, lens, codeLens))); + (field, value) -> { + DexField rewrittenField = lens.lookupField(field, codeLens); + rewrittenState.put( + rewrittenField, + value.rewrittenWithLens(appView, rewrittenField.getType(), lens, codeLens)); + }); return new NonEmptyObjectState(rewrittenState); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/And.java b/src/main/java/com/android/tools/r8/ir/code/And.java index ef15dfa..03085c1 100644 --- a/src/main/java/com/android/tools/r8/ir/code/And.java +++ b/src/main/java/com/android/tools/r8/ir/code/And.java
@@ -109,7 +109,7 @@ int result = foldIntegers( left.asSingleNumberValue().getIntValue(), right.asSingleNumberValue().getIntValue()); - return appView.abstractValueFactory().createSingleNumberValue(result); + return appView.abstractValueFactory().createSingleNumberValue(result, getOutType()); } if (left.hasDefinitelySetAndUnsetBitsInformation() && right.hasDefinitelySetAndUnsetBitsInformation()) {
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 a926e50..654aff1 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
@@ -154,7 +154,7 @@ double result = foldDouble(leftConst.getDoubleValue(), rightConst.getDoubleValue()); newConst = Double.doubleToLongBits(result); } - return appView.abstractValueFactory().createSingleNumberValue(newConst); + return appView.abstractValueFactory().createSingleNumberValue(newConst, getOutType()); } return AbstractValue.unknown(); }
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 5c64fc6..fae64e1 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
@@ -191,7 +191,7 @@ result = (int) Math.signum(left - right); } } - return appView.abstractValueFactory().createSingleNumberValue(result); + return appView.abstractValueFactory().createSingleNumberValue(result, getOutType()); } else if (leftAbstractValue.isConstantOrNonConstantNumberValue() && rightAbstractValue.isConstantOrNonConstantNumberValue()) { return buildLatticeResult( @@ -212,7 +212,7 @@ // Use min value as representative when values cannot overlap. int result = Integer.signum(Long.compare(leftRange.getMinInclusive(), rightRange.getMinInclusive())); - return appView.abstractValueFactory().createSingleNumberValue(result); + return appView.abstractValueFactory().createSingleNumberValue(result, getOutType()); } @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 72b2952..55f4136 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
@@ -22,6 +22,7 @@ import com.android.tools.r8.ir.analysis.VerifyTypesHelper; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.analysis.value.AbstractValue; +import com.android.tools.r8.ir.analysis.value.AbstractValueFactory; import com.android.tools.r8.ir.conversion.CfBuilder; import com.android.tools.r8.ir.conversion.DexBuilder; import com.android.tools.r8.lightir.LirBuilder; @@ -337,7 +338,10 @@ if (outValue.hasLocalInfo()) { return AbstractValue.unknown(); } - return appView.abstractValueFactory().createSingleNumberValue(value); + AbstractValueFactory factory = appView.abstractValueFactory(); + return getOutType().isReferenceType() + ? factory.createNullValue(getOutType()) + : factory.createSingleNumberValue(value, getOutType()); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java index fa5469f..aad507a 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.cf.LoadStoreHelper; import com.android.tools.r8.cf.TypeVerificationHelper; import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexEncodedMethod; @@ -17,6 +18,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.LookupResult; import com.android.tools.r8.graph.MethodResolutionResult; +import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet; @@ -30,6 +32,7 @@ import com.android.tools.r8.ir.optimize.DefaultInliningOracle; import com.android.tools.r8.ir.optimize.Inliner.InlineAction; import com.android.tools.r8.ir.optimize.Inliner.Reason; +import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo; import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter; import com.android.tools.r8.ir.regalloc.RegisterAllocator; import com.android.tools.r8.shaking.AppInfoWithLiveness; @@ -37,7 +40,6 @@ import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.ImmutableList; import java.util.Arrays; -import java.util.BitSet; import java.util.Collections; import java.util.List; @@ -113,6 +115,11 @@ return this; } + public MethodResolutionResult resolveMethod( + AppView<? extends AppInfoWithClassHierarchy> appView) { + return appView.appInfo().resolveMethod(method, getInterfaceBit()); + } + // In subclasses, e.g., invoke-virtual or invoke-super, use a narrower receiver type by using // receiver type and calling context---the holder of the method where the current invocation is. // TODO(b/140204899): Refactor lookup methods to be defined in a single place. @@ -266,13 +273,25 @@ @Override public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) { + if (!appView.hasClassHierarchy()) { + return false; + } + AppView<? extends AppInfoWithClassHierarchy> appViewWithClassHierarchy = + appView.withClassHierarchy(); + SingleResolutionResult<?> resolutionResult = + resolveMethod(appViewWithClassHierarchy).asSingleResolution(); + if (resolutionResult == null) { + return false; + } DexClassAndMethod singleTarget = lookupSingleTarget(appView, context); - if (singleTarget != null) { - BitSet nonNullParamOrThrow = - singleTarget.getDefinition().getOptimizationInfo().getNonNullParamOrThrow(); - if (nonNullParamOrThrow != null) { - int argumentIndex = inValues.indexOf(value); - return argumentIndex >= 0 && nonNullParamOrThrow.get(argumentIndex); + MethodOptimizationInfo optimizationInfo = + resolutionResult.getOptimizationInfo(appView, this, singleTarget); + if (optimizationInfo.hasNonNullParamOrThrow()) { + for (int argumentIndex = 0; argumentIndex < arguments().size(); argumentIndex++) { + if (value == getArgument(argumentIndex) + && optimizationInfo.getNonNullParamOrThrow().get(argumentIndex)) { + return true; + } } } return false;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java index 27e709e..2521043 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -250,10 +250,7 @@ appView.withClassHierarchy(); SingleResolutionResult<?> resolutionResult = - appViewWithClassHierarchy - .appInfo() - .resolveMethodLegacy(getInvokedMethod(), getInterfaceBit()) - .asSingleResolution(); + resolveMethod(appViewWithClassHierarchy).asSingleResolution(); if (resolutionResult == null) { return true; } @@ -275,6 +272,12 @@ // Find the target and check if the invoke may have side effects. DexClassAndMethod singleTarget = lookupSingleTarget(appView, context); + MethodOptimizationInfo optimizationInfo = + resolutionResult.getOptimizationInfo(appView, this, singleTarget); + if (!optimizationInfo.mayHaveSideEffects(this, appView.options())) { + return false; + } + if (singleTarget == null) { return true; } @@ -291,10 +294,8 @@ return false; } - DexEncodedMethod singleTargetDefinition = singleTarget.getDefinition(); - MethodOptimizationInfo optimizationInfo = singleTargetDefinition.getOptimizationInfo(); if (assumption.canIgnoreInstanceFieldAssignmentsToReceiver() - && singleTargetDefinition.isInstanceInitializer()) { + && singleTarget.getDefinition().isInstanceInitializer()) { assert isInvokeDirect(); InstanceInitializerInfo initializerInfo = optimizationInfo.getInstanceInitializerInfo(asInvokeDirect()); @@ -303,6 +304,6 @@ } } - return optimizationInfo.mayHaveSideEffects(this, appView.options()); + return true; } }
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 3bc6933..a197004 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
@@ -132,7 +132,7 @@ } newConst = foldLongs(leftConst.getLongValue(), right); } - return appView.abstractValueFactory().createSingleNumberValue(newConst); + return appView.abstractValueFactory().createSingleNumberValue(newConst, getOutType()); } else { if (type == NumericType.INT) { return foldIntegers(leftAbstractValue, rightAbstractValue, appView);
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 222973a..46b1c59 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
@@ -103,7 +103,7 @@ assert type == NumericType.DOUBLE; newConst = Double.doubleToLongBits(-sourceConst.getDoubleValue()); } - return appView.abstractValueFactory().createSingleNumberValue(newConst); + return appView.abstractValueFactory().createSingleNumberValue(newConst, getOutType()); } return AbstractValue.unknown(); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java index ea07979..483aa87 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Not.java +++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -56,7 +56,7 @@ assert this.type == NumericType.LONG; newConst = ~sourceConst.getLongValue(); } - return appView.abstractValueFactory().createSingleNumberValue(newConst); + return appView.abstractValueFactory().createSingleNumberValue(newConst, getOutType()); } return AbstractValue.unknown(); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Or.java b/src/main/java/com/android/tools/r8/ir/code/Or.java index 8765cb5..bca7903 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Or.java +++ b/src/main/java/com/android/tools/r8/ir/code/Or.java
@@ -108,7 +108,7 @@ int result = foldIntegers( left.asSingleNumberValue().getIntValue(), right.asSingleNumberValue().getIntValue()); - return appView.abstractValueFactory().createSingleNumberValue(result); + return appView.abstractValueFactory().createSingleNumberValue(result, getOutType()); } if (left.hasDefinitelySetAndUnsetBitsInformation() && right.hasDefinitelySetAndUnsetBitsInformation()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shl.java b/src/main/java/com/android/tools/r8/ir/code/Shl.java index 72f8fad..747daf6 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Shl.java +++ b/src/main/java/com/android/tools/r8/ir/code/Shl.java
@@ -102,7 +102,7 @@ } if (left.isSingleNumberValue()) { int result = foldIntegers(left.asSingleNumberValue().getIntValue(), rightConst); - return appView.abstractValueFactory().createSingleNumberValue(result); + return appView.abstractValueFactory().createSingleNumberValue(result, getOutType()); } if (left.hasDefinitelySetAndUnsetBitsInformation() && rightConst > 0) { // Shift the known bits and add that we now know that the lowermost n bits are definitely
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shr.java b/src/main/java/com/android/tools/r8/ir/code/Shr.java index 74ec192..e18a846 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Shr.java +++ b/src/main/java/com/android/tools/r8/ir/code/Shr.java
@@ -102,7 +102,7 @@ } if (left.isSingleNumberValue()) { int result = foldIntegers(left.asSingleNumberValue().getIntValue(), rightConst); - return appView.abstractValueFactory().createSingleNumberValue(result); + return appView.abstractValueFactory().createSingleNumberValue(result, getOutType()); } if (left.hasDefinitelySetAndUnsetBitsInformation()) { return appView
diff --git a/src/main/java/com/android/tools/r8/ir/code/Ushr.java b/src/main/java/com/android/tools/r8/ir/code/Ushr.java index f93eefc..104b90d 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Ushr.java +++ b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
@@ -108,7 +108,7 @@ } if (left.isSingleNumberValue()) { int result = foldIntegers(left.asSingleNumberValue().getIntValue(), rightConst); - return appView.abstractValueFactory().createSingleNumberValue(result); + return appView.abstractValueFactory().createSingleNumberValue(result, getOutType()); } if (left.hasDefinitelySetAndUnsetBitsInformation() && rightConst > 0) { // Shift the known bits information and add that we now know that the uppermost n bits are
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java index c0253ac..003b119 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Value.java +++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -867,7 +867,7 @@ } if (getType().nullability().isDefinitelyNull()) { - return appView.abstractValueFactory().createNullValue(); + return appView.abstractValueFactory().createNullValue(getType()); } Value root = getAliasedValue();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Xor.java b/src/main/java/com/android/tools/r8/ir/code/Xor.java index a675feb..5116990 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Xor.java +++ b/src/main/java/com/android/tools/r8/ir/code/Xor.java
@@ -102,7 +102,7 @@ int result = foldIntegers( left.asSingleNumberValue().getIntValue(), right.asSingleNumberValue().getIntValue()); - return appView.abstractValueFactory().createSingleNumberValue(result); + return appView.abstractValueFactory().createSingleNumberValue(result, getOutType()); } if (left.hasDefinitelySetAndUnsetBitsInformation() && right.hasDefinitelySetAndUnsetBitsInformation()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java index a725f36..1812e1c 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java
@@ -32,7 +32,12 @@ @Override public SingleNumberValue getValue(AppView<?> appView) { - return appView.abstractValueFactory().createSingleNumberValue(value); + return appView.abstractValueFactory().createSingleNumberValue(value, TypeElement.getInt()); + } + + @Override + public boolean isUnused() { + return false; } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java index 44452f6..e7fcc11 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.analysis.type.TypeElement; -import com.android.tools.r8.ir.analysis.value.SingleNumberValue; +import com.android.tools.r8.ir.analysis.value.SingleConstValue; public abstract class ExtraParameter { @@ -16,7 +16,9 @@ public abstract TypeElement getTypeElement(AppView<?> appView, DexType argType); - public abstract SingleNumberValue getValue(AppView<?> appView); + public abstract SingleConstValue getValue(AppView<?> appView); + + public abstract boolean isUnused(); @Override public abstract boolean equals(Object obj);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedIntParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedIntParameter.java new file mode 100644 index 0000000..dc1d09c --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedIntParameter.java
@@ -0,0 +1,44 @@ +// Copyright (c) 2023, 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.conversion; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.analysis.type.TypeElement; +import com.android.tools.r8.ir.analysis.value.SingleNumberValue; + +public class ExtraUnusedIntParameter extends ExtraParameter { + + @Override + public DexType getType(DexItemFactory dexItemFactory) { + return dexItemFactory.intType; + } + + @Override + public TypeElement getTypeElement(AppView<?> appView, DexType argType) { + return TypeElement.getInt(); + } + + @Override + public SingleNumberValue getValue(AppView<?> appView) { + return appView.abstractValueFactory().createZeroValue(); + } + + @Override + public boolean isUnused() { + return true; + } + + @Override + @SuppressWarnings("EqualsGetClass") + public boolean equals(Object obj) { + return obj != null && getClass() == obj.getClass(); + } + + @Override + public int hashCode() { + return 0; + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java index 41f3237..2ef4bcc 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.analysis.type.Nullability; import com.android.tools.r8.ir.analysis.type.TypeElement; -import com.android.tools.r8.ir.analysis.value.SingleNumberValue; +import com.android.tools.r8.ir.analysis.value.SingleNullValue; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -20,6 +20,7 @@ private final DexType type; public ExtraUnusedNullParameter(DexType type) { + assert type.isReferenceType(); this.type = type; } @@ -53,8 +54,13 @@ } @Override - public SingleNumberValue getValue(AppView<?> appView) { - return appView.abstractValueFactory().createNullValue(); + public SingleNullValue getValue(AppView<?> appView) { + return appView.abstractValueFactory().createNullValue(type); + } + + @Override + public boolean isUnused() { + return true; } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java index 63a52e5..a3ca7c5 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -601,7 +601,7 @@ int newArgumentIndex = argumentsInfo.getNewArgumentIndex(argumentIndex, numberOfRemovedArguments); DexType extraArgumentType = method.getArgumentType(newArgumentIndex); - if (extraParameter instanceof ExtraUnusedNullParameter) { + if (extraParameter.isUnused()) { addExtraUnusedArgument(extraArgumentType); } else { addNonThisArgument(register, extraParameter.getTypeElement(appView, extraArgumentType));
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java index cd1dbbf..391deb2 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -64,7 +64,7 @@ import com.android.tools.r8.ir.analysis.type.DynamicType; import com.android.tools.r8.ir.analysis.type.Nullability; import com.android.tools.r8.ir.analysis.type.TypeElement; -import com.android.tools.r8.ir.analysis.value.SingleNumberValue; +import com.android.tools.r8.ir.analysis.value.SingleConstValue; import com.android.tools.r8.ir.analysis.value.SingleValue; import com.android.tools.r8.ir.code.Argument; import com.android.tools.r8.ir.code.Assume; @@ -501,7 +501,7 @@ newOutValue = makeOutValue(invoke, code, graphLens, codeLens); } - Map<SingleNumberValue, Map<DexType, Value>> parameterMap = new IdentityHashMap<>(); + Map<SingleConstValue, Map<DexType, Value>> parameterMap = new IdentityHashMap<>(); int extraArgumentIndex = numberOfArguments - prototypeChanges.getExtraParameters().size(); @@ -512,19 +512,19 @@ actualTarget.getArgumentType( newExtraArgumentIndex, actualInvokeType.isStatic()); - SingleNumberValue numberValue = parameter.getValue(appView); + SingleConstValue singleConstValue = parameter.getValue(appView); // Try to find an existing constant instruction, otherwise generate a new one. InstructionListIterator finalIterator = iterator; Value value = parameterMap - .computeIfAbsent(numberValue, ignore -> new IdentityHashMap<>()) + .computeIfAbsent(singleConstValue, ignore -> new IdentityHashMap<>()) .computeIfAbsent( extraArgumentType, ignore -> { finalIterator.previous(); Instruction instruction = - numberValue.createMaterializingInstruction( + singleConstValue.createMaterializingInstruction( appView, code, TypeAndLocalInfoSupplier.create(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java index 7327760..e5a0728 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -143,10 +143,14 @@ } } - default void unsetOptimizationInfoForThrowNullMethod(ProgramMethod method) { - unsetOptimizationInfoForAbstractMethod(method); - methodNeverReturnsNormally(method); - setUnusedArguments( - method, BitSetUtils.createFilled(true, method.getDefinition().getNumberOfArguments())); + default void unsetOptimizationInfoForThrowNullMethod(AppView<?> appView, ProgramMethod method) { + if (!appView.hasClassHierarchy() || appView.getKeepInfo(method).isPinned(appView.options())) { + assert method.getOptimizationInfo().isDefault(); + } else { + unsetOptimizationInfoForAbstractMethod(method); + methodNeverReturnsNormally(method); + setUnusedArguments( + method, BitSetUtils.createFilled(true, method.getDefinition().getNumberOfArguments())); + } } }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java index cc12b6e..148e7df 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -20,6 +20,7 @@ import com.android.tools.r8.ir.conversion.passes.FilledNewArrayRewriter; import com.android.tools.r8.ir.optimize.ConstantCanonicalizer; import com.android.tools.r8.ir.optimize.DeadCodeRemover; +import com.android.tools.r8.ir.optimize.info.MethodResolutionOptimizationInfoAnalysis; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed; import com.android.tools.r8.lightir.LirCode; import com.android.tools.r8.optimize.MemberRebindingIdentityLens; @@ -165,6 +166,9 @@ outliner.rewriteWithLens(); + MethodResolutionOptimizationInfoAnalysis.run( + appView, executorService, postMethodProcessorBuilder); + { timing.begin("IR conversion phase 2"); MethodProcessorEventConsumer eventConsumer = @@ -197,6 +201,8 @@ timing.end(); } + appView.clearMethodResolutionOptimizationInfoCollection(); + enumUnboxer.unsetRewriter(); // All the code that should be impacted by the lenses inserted between phase 1 and phase 2
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java index f8f6235..b9b34ad 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java
@@ -6,9 +6,11 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppInfo; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.CatchHandlers; @@ -270,18 +272,22 @@ } } - if (!instruction.isInvokeMethod()) { + if (!appView.hasClassHierarchy() || !instruction.isInvokeMethod()) { continue; } + AppView<? extends AppInfoWithClassHierarchy> appViewWithClassHierarchy = + appView.withClassHierarchy(); InvokeMethod invoke = instruction.asInvokeMethod(); - DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, code.context()); - if (singleTarget == null) { + SingleResolutionResult<?> resolutionResult = + invoke.resolveMethod(appViewWithClassHierarchy).asSingleResolution(); + if (resolutionResult == null) { continue; } + DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, code.context()); MethodOptimizationInfo optimizationInfo = - singleTarget.getDefinition().getOptimizationInfo(); + resolutionResult.getOptimizationInfo(appViewWithClassHierarchy, invoke, singleTarget); // If the invoke instruction is a null check, we can remove it. boolean isNullCheck = false;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java index 8e23208..82314cf 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -1088,6 +1088,16 @@ } private void initializeAndroidQMethodProviders(DexItemFactory factory) { + // BigDecimal BigDecimal.stripTrailingZeros() + DexType bigDecimal = factory.createType("Ljava/math/BigDecimal;"); + addProvider( + new StatifyingMethodWithForwardingGenerator( + factory.createMethod( + bigDecimal, factory.createProto(bigDecimal), "stripTrailingZeros"), + BackportedMethods::BigDecimalMethods_stripTrailingZeros, + "stripTrailingZeros", + bigDecimal)); + // void android.drm.DrmManagerClient.close() addProvider( new InvokeRewriter(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java index 7d65007..6c73cde 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -35,6 +35,7 @@ import com.android.tools.r8.cf.code.CfReturn; import com.android.tools.r8.cf.code.CfReturnVoid; import com.android.tools.r8.cf.code.CfStackInstruction; +import com.android.tools.r8.cf.code.CfStaticFieldRead; import com.android.tools.r8.cf.code.CfStore; import com.android.tools.r8.cf.code.CfThrow; import com.android.tools.r8.cf.code.CfTryCatch; @@ -78,6 +79,8 @@ factory.createSynthesizedType("Ljava/lang/reflect/Constructor;"); factory.createSynthesizedType("Ljava/lang/reflect/InvocationTargetException;"); factory.createSynthesizedType("Ljava/lang/reflect/Method;"); + factory.createSynthesizedType("Ljava/math/BigDecimal;"); + factory.createSynthesizedType("Ljava/math/BigInteger;"); factory.createSynthesizedType("Ljava/util/AbstractMap$SimpleImmutableEntry;"); factory.createSynthesizedType("Ljava/util/ArrayList;"); factory.createSynthesizedType("Ljava/util/Arrays;"); @@ -425,6 +428,69 @@ ImmutableList.of()); } + public static CfCode BigDecimalMethods_stripTrailingZeros( + DexItemFactory factory, DexMethod method) { + CfLabel label0 = new CfLabel(); + CfLabel label1 = new CfLabel(); + CfLabel label2 = new CfLabel(); + CfLabel label3 = new CfLabel(); + return new CfCode( + method.holder, + 4, + 1, + ImmutableList.of( + label0, + new CfLoad(ValueType.OBJECT, 0), + new CfInvoke( + 182, + factory.createMethod( + factory.createType("Ljava/math/BigDecimal;"), + factory.createProto(factory.intType), + factory.createString("signum")), + false), + new CfIf(IfType.NE, ValueType.INT, label2), + label1, + new CfNew(factory.createType("Ljava/math/BigDecimal;")), + new CfStackInstruction(CfStackInstruction.Opcode.Dup), + new CfStaticFieldRead( + factory.createField( + factory.createType("Ljava/math/BigInteger;"), + factory.createType("Ljava/math/BigInteger;"), + factory.createString("ZERO"))), + new CfConstNumber(0, ValueType.INT), + new CfInvoke( + 183, + factory.createMethod( + factory.createType("Ljava/math/BigDecimal;"), + factory.createProto( + factory.voidType, + factory.createType("Ljava/math/BigInteger;"), + factory.intType), + factory.createString("<init>")), + false), + new CfReturn(ValueType.OBJECT), + label2, + new CfFrame( + new Int2ObjectAVLTreeMap<>( + new int[] {0}, + new FrameType[] { + FrameType.initializedNonNullReference( + factory.createType("Ljava/math/BigDecimal;")) + })), + new CfLoad(ValueType.OBJECT, 0), + new CfInvoke( + 182, + factory.createMethod( + factory.createType("Ljava/math/BigDecimal;"), + factory.createProto(factory.createType("Ljava/math/BigDecimal;")), + factory.createString("stripTrailingZeros")), + false), + new CfReturn(ValueType.OBJECT), + label3), + ImmutableList.of(), + ImmutableList.of()); + } + public static CfCode BooleanMethods_compare(DexItemFactory factory, DexMethod method) { CfLabel label0 = new CfLabel(); CfLabel label1 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java index de42de1..e0af2eb 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
@@ -26,6 +26,8 @@ boolean isLibraryCompilation(); + String getIdentifier(); + String getJsonSource(); String getSynthesizedLibraryClassesPackagePrefix();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java index 325cdb0..fcd5d0a 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
@@ -69,6 +69,7 @@ return rewritingFlags; } + @Override public String getIdentifier() { return topLevelFlags.getIdentifier(); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java index 3507445..419c71e 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
@@ -73,6 +73,7 @@ return topLevelFlags.getSynthesizedLibraryClassesPackagePrefix(); } + @Override public String getIdentifier() { return topLevelFlags.getIdentifier(); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java index f1092d4..f263c2e 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
@@ -4,26 +4,18 @@ package com.android.tools.r8.ir.desugar.desugaredlibrary.lint; +import com.android.tools.r8.ClassFileResourceProvider; +import com.android.tools.r8.ProgramResourceProvider; import com.android.tools.r8.StringResource; -import com.android.tools.r8.dex.ApplicationReader; -import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser; import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification; import com.android.tools.r8.utils.AndroidApiLevel; -import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Reporter; -import com.android.tools.r8.utils.ThreadUtils; -import com.android.tools.r8.utils.Timing; -import com.google.common.collect.ImmutableList; -import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Collection; -import java.util.concurrent.ExecutorService; public abstract class AbstractGenerateFiles { @@ -38,86 +30,36 @@ .setAllowReadingEmptyArtProfileProvidersMultipleTimesForTesting(true) .getOptions(); - final MachineDesugaredLibrarySpecification desugaredLibrarySpecification; - final Path desugaredLibrarySpecificationPath; - final Collection<Path> desugaredLibraryImplementation; + final DesugaredLibrarySpecification desugaredLibrarySpecification; + final StringResource desugaredLibrarySpecificationResource; + final Collection<ProgramResourceProvider> desugaredLibraryImplementation; final Path output; - final Path androidJar; + final Collection<ClassFileResourceProvider> androidJar; AbstractGenerateFiles( - String desugarConfigurationPath, - String desugarImplementationPath, - String output, - String androidJarPath) - throws Exception { - this( - Paths.get(desugarConfigurationPath), - ImmutableList.of(Paths.get(desugarImplementationPath)), - Paths.get(output), - Paths.get(androidJarPath)); - } - - AbstractGenerateFiles( - Path desugarConfigurationPath, - Collection<Path> desugarImplementationPath, + StringResource desugaredLibrarySpecificationResource, + Collection<ProgramResourceProvider> desugarImplementation, Path output, - Path androidJar) - throws Exception { + Collection<ClassFileResourceProvider> androidJar) { assert androidJar != null; - this.desugaredLibrarySpecificationPath = desugarConfigurationPath; - DesugaredLibrarySpecification specification = - readDesugaredLibraryConfiguration(desugarConfigurationPath); + this.desugaredLibrarySpecificationResource = desugaredLibrarySpecificationResource; this.androidJar = androidJar; - DexApplication app = createApp(androidJar, options); - this.desugaredLibrarySpecification = specification.toMachineSpecification(app, Timing.empty()); - this.desugaredLibraryImplementation = desugarImplementationPath; + this.desugaredLibrarySpecification = readDesugaredLibrarySpecification(); + this.desugaredLibraryImplementation = desugarImplementation; this.output = output; } - private DesugaredLibrarySpecification readDesugaredLibraryConfiguration( - Path desugarConfigurationPath) { + private DesugaredLibrarySpecification readDesugaredLibrarySpecification() { + if (desugaredLibrarySpecificationResource == null) { + return MachineDesugaredLibrarySpecification.empty(); + } return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification( - StringResource.fromFile(desugarConfigurationPath), + desugaredLibrarySpecificationResource, factory, reporter, false, AndroidApiLevel.B.getLevel()); } - private static DexApplication createApp(Path androidJar, InternalOptions options) - throws IOException { - AndroidApp.Builder builder = AndroidApp.builder(); - AndroidApp inputApp = builder.addLibraryFiles(androidJar).build(); - ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty()); - ExecutorService executorService = ThreadUtils.getExecutorService(options); - assert !options.ignoreJavaLibraryOverride; - options.ignoreJavaLibraryOverride = true; - DexApplication app = applicationReader.read(executorService); - options.ignoreJavaLibraryOverride = false; - return app; - } - abstract AndroidApiLevel run() throws Exception; - - // TODO(b/289365156): Move this out. - private static String getFallBackAndroidJarPath(AndroidApiLevel apiLevel) { - String jar = - apiLevel == AndroidApiLevel.MASTER - ? "third_party/android_jar/lib-master/android.jar" - : String.format("third_party/android_jar/lib-v%d/android.jar", apiLevel.getLevel()); - Path jarPath = Paths.get(jar); - if (!Files.exists(jarPath)) { - throw new RuntimeException( - "Generate files tools should pass a valid recent android.jar as parameter if used outside" - + " of the r8 repository. Missing file: " - + jarPath); - } - return jar; - } - - static String getAndroidJarPath(String[] args, int fullLength) { - return args.length == fullLength - ? args[fullLength - 1] - : getFallBackAndroidJarPath(MAX_TESTED_ANDROID_API_LEVEL); - } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/DesugaredMethodsList.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/DesugaredMethodsList.java index 0a1a614..c2261d4 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/DesugaredMethodsList.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/DesugaredMethodsList.java
@@ -6,37 +6,68 @@ import static java.lang.Integer.parseInt; +import com.android.tools.r8.ArchiveClassFileProvider; +import com.android.tools.r8.ArchiveProgramResourceProvider; +import com.android.tools.r8.ClassFileResourceProvider; import com.android.tools.r8.Keep; +import com.android.tools.r8.ProgramResourceProvider; +import com.android.tools.r8.StringConsumer; +import com.android.tools.r8.StringResource; +import com.android.tools.r8.Version; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.StringUtils; -import java.nio.file.Path; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.List; @Keep public class DesugaredMethodsList extends GenerateDesugaredLibraryLintFiles { private final AndroidApiLevel minApi; - private DesugaredMethodsList( + private final StringConsumer outputConsumer; + + DesugaredMethodsList( int minApi, - String desugarConfigurationPath, - String desugarImplementationPath, - String ouputFile, - String androidJarPath) - throws Exception { - super(desugarConfigurationPath, desugarImplementationPath, ouputFile, androidJarPath); + StringResource desugarConfiguration, + Collection<ProgramResourceProvider> desugarImplementation, + StringConsumer outputConsumer, + Collection<ClassFileResourceProvider> androidJar) { + super(desugarConfiguration, desugarImplementation, null, androidJar); this.minApi = AndroidApiLevel.getAndroidApiLevel(minApi); + this.outputConsumer = outputConsumer; + } + + public static void run(DesugaredMethodsListCommand command) throws IOException { + if (command.isHelp()) { + System.out.println(DesugaredMethodsListCommand.getUsageMessage()); + return; + } + if (command.isVersion()) { + System.out.println("DesugaredMethodsList " + Version.getVersionString()); + return; + } + new DesugaredMethodsList( + command.getMinApi(), + command.getDesugarLibrarySpecification(), + command.getDesugarLibraryImplementation(), + command.getOutputConsumer(), + command.getLibrary()) + .run(); } @Override - public AndroidApiLevel run() throws Exception { + public AndroidApiLevel run() throws IOException { AndroidApiLevel compilationLevel = desugaredLibrarySpecification.getRequiredCompilationApiLevel(); SupportedClasses supportedMethods = new SupportedClassesGenerator(options, androidJar, minApi, true) - .run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath); + .run(desugaredLibraryImplementation, desugaredLibrarySpecificationResource); System.out.println( "Generating lint files for " - + desugaredLibrarySpecification.getIdentifier() + + getDebugIdentifier() + " (compile API " + compilationLevel + ")"); @@ -45,24 +76,47 @@ } @Override - Path lintFile( - AndroidApiLevel compilationApiLevel, AndroidApiLevel minApiLevel, String extension) { - return output; + void writeOutput( + AndroidApiLevel compilationApiLevel, + AndroidApiLevel minApiLevel, + List<String> desugaredApisSignatures) { + for (String desugaredApisSignature : desugaredApisSignatures) { + outputConsumer.accept(desugaredApisSignature, options.reporter); + } + outputConsumer.finished(options.reporter); + } + + private static StringResource getSpecificationArg(String arg) { + return arg == null ? null : StringResource.fromFile(Paths.get(arg)); + } + + private static Collection<ProgramResourceProvider> getImplementationArg(String arg) { + if (arg == null) { + return ImmutableList.of(); + } + return ImmutableList.of(ArchiveProgramResourceProvider.fromArchive(Paths.get(arg))); } public static void main(String[] args) throws Exception { if (args.length == 4 || args.length == 5) { - new DesugaredMethodsList( - parseInt(args[0]), args[1], args[2], args[3], getAndroidJarPath(args, 5)) - .run(); + DesugaredMethodsListCommand.Builder builder = + DesugaredMethodsListCommand.builder() + .setMinApi(parseInt(args[0])) + .setDesugarLibrarySpecification(getSpecificationArg(args[1])) + .setOutputPath(Paths.get(args[3])); + for (ProgramResourceProvider programResourceProvider : getImplementationArg(args[2])) { + builder.addDesugarLibraryImplementation(programResourceProvider); + } + builder.addLibrary(new ArchiveClassFileProvider(Paths.get(args[4]))); + DesugaredMethodsList.run(builder.build()); return; } throw new RuntimeException( StringUtils.joinLines( "Invalid invocation.", "Usage: DesugaredMethodList <min-api> <desugar configuration> " - + "<desugar implementation> <output file> [<android jar path for Android " + + "<desugar implementation> <output file> <android jar path for Android " + MAX_TESTED_ANDROID_API_LEVEL - + " or higher>]")); + + " or higher>")); } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/DesugaredMethodsListCommand.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/DesugaredMethodsListCommand.java new file mode 100644 index 0000000..924a0e6 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/DesugaredMethodsListCommand.java
@@ -0,0 +1,253 @@ +// Copyright (c) 2023, 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.desugar.desugaredlibrary.lint; + +import com.android.tools.r8.ArchiveClassFileProvider; +import com.android.tools.r8.ArchiveProgramResourceProvider; +import com.android.tools.r8.ClassFileResourceProvider; +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.Keep; +import com.android.tools.r8.ParseFlagInfo; +import com.android.tools.r8.ParseFlagInfoImpl; +import com.android.tools.r8.ParseFlagPrinter; +import com.android.tools.r8.ProgramResourceProvider; +import com.android.tools.r8.StringConsumer; +import com.android.tools.r8.StringResource; +import com.android.tools.r8.errors.CompilationError; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@Keep +public class DesugaredMethodsListCommand { + + private final boolean help; + private final boolean version; + private final int minApi; + private final StringResource desugarLibrarySpecification; + private final Collection<ProgramResourceProvider> desugarLibraryImplementation; + private final StringConsumer outputConsumer; + private final Collection<ClassFileResourceProvider> library; + + DesugaredMethodsListCommand( + int minApi, + StringResource desugarLibrarySpecification, + Collection<ProgramResourceProvider> desugarLibraryImplementation, + StringConsumer outputConsumer, + Collection<ClassFileResourceProvider> library) { + this.help = false; + this.version = false; + this.minApi = minApi; + this.desugarLibrarySpecification = desugarLibrarySpecification; + this.desugarLibraryImplementation = desugarLibraryImplementation; + this.outputConsumer = outputConsumer; + this.library = library; + } + + DesugaredMethodsListCommand(boolean help, boolean version) { + this.help = help; + this.version = version; + this.minApi = -1; + this.desugarLibrarySpecification = null; + this.desugarLibraryImplementation = null; + this.outputConsumer = null; + this.library = null; + } + + public int getMinApi() { + return minApi; + } + + public StringResource getDesugarLibrarySpecification() { + return desugarLibrarySpecification; + } + + public Collection<ProgramResourceProvider> getDesugarLibraryImplementation() { + return desugarLibraryImplementation; + } + + public StringConsumer getOutputConsumer() { + return outputConsumer; + } + + public Collection<ClassFileResourceProvider> getLibrary() { + return library; + } + + public boolean isHelp() { + return help; + } + + public boolean isVersion() { + return version; + } + + public static String getUsageMessage() { + StringBuilder builder = new StringBuilder(); + StringUtils.appendLines(builder, "Usage: desugaredmethods [options] where options are:"); + new ParseFlagPrinter() + .addFlags(ImmutableList.copyOf(DesugaredMethodsListCommandParser.getFlags())) + .appendLinesToBuilder(builder); + return builder.toString(); + } + + public static Builder builder() { + return new Builder(); + } + + @Keep + public static class Builder { + + private int minApi = AndroidApiLevel.B.getLevel(); + private StringResource desugarLibrarySpecification = null; + private Collection<ProgramResourceProvider> desugarLibraryImplementation = new ArrayList<>(); + private StringConsumer outputConsumer; + private Collection<ClassFileResourceProvider> library = new ArrayList<>(); + + private boolean help = false; + private boolean version = false; + + public Builder setMinApi(int minApi) { + this.minApi = minApi; + return this; + } + + public Builder setDesugarLibrarySpecification(StringResource desugarLibrarySpecification) { + this.desugarLibrarySpecification = desugarLibrarySpecification; + return this; + } + + public Builder setOutputConsumer(StringConsumer outputConsumer) { + this.outputConsumer = outputConsumer; + return this; + } + + public Builder setOutputPath(Path outputPath) { + this.outputConsumer = + new StringConsumer.FileConsumer(outputPath) { + @Override + public void accept(String string, DiagnosticsHandler handler) { + super.accept(string, handler); + super.accept(System.lineSeparator(), handler); + } + }; + return this; + } + + public Builder addDesugarLibraryImplementation( + ProgramResourceProvider programResourceProvider) { + desugarLibraryImplementation.add(programResourceProvider); + return this; + } + + public Builder addLibrary(ClassFileResourceProvider classFileResourceProvider) { + library.add(classFileResourceProvider); + return this; + } + + public Builder setHelp() { + this.help = true; + return this; + } + + public Builder setVersion() { + this.version = true; + return this; + } + + public DesugaredMethodsListCommand build() { + // The min-api level defaults to 1, it's always present. + // If desugarLibraryImplementation is empty, this generates only the backported method list. + + if (help || version) { + return new DesugaredMethodsListCommand(help, version); + } + + if (desugarLibrarySpecification != null && library.isEmpty()) { + throw new CompilationError("With desugared library configuration a library is required"); + } + + if (!desugarLibraryImplementation.isEmpty() && desugarLibrarySpecification == null) { + throw new CompilationError( + "desugarLibrarySpecification is required when desugared library implementation is" + + " present."); + } + + if (outputConsumer == null) { + outputConsumer = + new StringConsumer() { + @Override + public void accept(String string, DiagnosticsHandler handler) { + System.out.println(string); + } + + @Override + public void finished(DiagnosticsHandler handler) {} + }; + } + return new DesugaredMethodsListCommand( + minApi, + desugarLibrarySpecification, + desugarLibraryImplementation, + outputConsumer, + library); + } + } + + public static class DesugaredMethodsListCommandParser { + + static List<ParseFlagInfo> getFlags() { + return ImmutableList.<ParseFlagInfo>builder() + .add(ParseFlagInfoImpl.getOutput()) + .add(ParseFlagInfoImpl.getLib()) + .add(ParseFlagInfoImpl.getMinApi()) + .add(ParseFlagInfoImpl.getVersion("DesugaredMethods")) + .add(ParseFlagInfoImpl.getHelp()) + .add(ParseFlagInfoImpl.getDesugaredLib()) + .add( + ParseFlagInfoImpl.flag1( + "--desugared-lib-jar", "<file>", "Specify desugared library jar.")) + .build(); + } + + public DesugaredMethodsListCommand parse(String[] args, Origin origin) throws IOException { + DesugaredMethodsListCommand.Builder builder = DesugaredMethodsListCommand.builder(); + for (int i = 0; i < args.length; i += 2) { + String arg = args[i].trim(); + if (arg.length() == 0) { + continue; + } else if (arg.equals("--help")) { + builder.setHelp(); + continue; + } else if (arg.equals("--version")) { + builder.setVersion(); + continue; + } + String argValue = args[i + 1].trim(); + if (arg.equals("--min-api")) { + builder.setMinApi(Integer.parseInt(argValue)); + } else if (arg.equals("--desugared-lib")) { + builder.setDesugarLibrarySpecification(StringResource.fromFile(Paths.get(argValue))); + } else if (arg.equals("--desugared-lib-jar")) { + builder.addDesugarLibraryImplementation( + ArchiveProgramResourceProvider.fromArchive(Paths.get(argValue))); + } else if (arg.equals("--output")) { + builder.setOutputPath(Paths.get(argValue)); + } else if (arg.equals("--lib")) { + builder.addLibrary(new ArchiveClassFileProvider(Paths.get(argValue))); + } + } + + return builder.build(); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateDesugaredLibraryLintFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateDesugaredLibraryLintFiles.java index 16e9be8..49d979b 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateDesugaredLibraryLintFiles.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateDesugaredLibraryLintFiles.java
@@ -4,13 +4,20 @@ package com.android.tools.r8.ir.desugar.desugaredlibrary.lint; +import com.android.tools.r8.ArchiveClassFileProvider; +import com.android.tools.r8.ArchiveProgramResourceProvider; +import com.android.tools.r8.ClassFileResourceProvider; +import com.android.tools.r8.ProgramResourceProvider; +import com.android.tools.r8.StringResource; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MethodAnnotation; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.FileUtils; import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; import java.io.File; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -18,36 +25,18 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.Set; public class GenerateDesugaredLibraryLintFiles extends AbstractGenerateFiles { // Only recent versions of studio support the format with fields. private static final boolean FORMAT_WITH_FIELD = true; - public static GenerateDesugaredLibraryLintFiles createForTesting( - Path specification, Set<Path> implementation, Path outputDirectory, Path androidJar) - throws Exception { - return new GenerateDesugaredLibraryLintFiles( - specification, implementation, outputDirectory, androidJar); - } - public GenerateDesugaredLibraryLintFiles( - String desugarConfigurationPath, - String desugarImplementationPath, - String outputDirectory, - String androidJarPath) - throws Exception { - super(desugarConfigurationPath, desugarImplementationPath, outputDirectory, androidJarPath); - } - - GenerateDesugaredLibraryLintFiles( - Path desugarConfigurationPath, - Collection<Path> desugarImplementationPath, - Path outputDirectory, - Path androidJar) - throws Exception { - super(desugarConfigurationPath, desugarImplementationPath, outputDirectory, androidJar); + StringResource desugarConfiguration, + Collection<ProgramResourceProvider> desugarImplementation, + Path output, + Collection<ClassFileResourceProvider> androidJar) { + super(desugarConfiguration, desugarImplementation, output, androidJar); } private String lintBaseFileName( @@ -55,8 +44,9 @@ return "desugared_apis_" + compilationApiLevel.getLevel() + "_" + minApiLevel.getLevel(); } - Path lintFile(AndroidApiLevel compilationApiLevel, AndroidApiLevel minApiLevel, String extension) - throws Exception { + private Path lintFile( + AndroidApiLevel compilationApiLevel, AndroidApiLevel minApiLevel, String extension) + throws IOException { Path directory = output.resolve("compile_api_level_" + compilationApiLevel.getLevel()); Files.createDirectories(directory); return Paths.get( @@ -70,7 +60,7 @@ AndroidApiLevel compilationApiLevel, AndroidApiLevel minApiLevel, SupportedClasses supportedClasses) - throws Exception { + throws IOException { // Build a plain text file with the desugared APIs. List<String> desugaredApisSignatures = new ArrayList<>(); @@ -118,6 +108,14 @@ // Write a plain text file with the desugared APIs. desugaredApisSignatures.sort(Comparator.naturalOrder()); + writeOutput(compilationApiLevel, minApiLevel, desugaredApisSignatures); + } + + void writeOutput( + AndroidApiLevel compilationApiLevel, + AndroidApiLevel minApiLevel, + List<String> desugaredApisSignatures) + throws IOException { FileUtils.writeTextFile( lintFile(compilationApiLevel, minApiLevel, ".txt"), desugaredApisSignatures); } @@ -149,16 +147,22 @@ writeLintFiles(compilationApiLevel, minApiLevel, supportedMethods); } + String getDebugIdentifier() { + return desugaredLibrarySpecification.getIdentifier() == null + ? "backported methods only" + : desugaredLibrarySpecification.getIdentifier(); + } + @Override public AndroidApiLevel run() throws Exception { AndroidApiLevel compilationLevel = desugaredLibrarySpecification.getRequiredCompilationApiLevel(); SupportedClasses supportedMethods = new SupportedClassesGenerator(options, androidJar) - .run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath); + .run(desugaredLibraryImplementation, desugaredLibrarySpecificationResource); System.out.println( "Generating lint files for " - + desugaredLibrarySpecification.getIdentifier() + + getDebugIdentifier() + " (compile API " + compilationLevel + ")"); @@ -169,8 +173,12 @@ } public static void main(String[] args) throws Exception { - if (args.length == 3 || args.length == 4) { - new GenerateDesugaredLibraryLintFiles(args[0], args[1], args[2], getAndroidJarPath(args, 4)) + if (args.length == 4) { + new GenerateDesugaredLibraryLintFiles( + StringResource.fromFile(Paths.get(args[0])), + ImmutableList.of(ArchiveProgramResourceProvider.fromArchive(Paths.get(args[1]))), + Paths.get(args[2]), + ImmutableList.of(new ArchiveClassFileProvider(Paths.get(args[3])))) .run(); return; } @@ -178,8 +186,8 @@ StringUtils.joinLines( "Invalid invocation.", "Usage: GenerateDesugaredLibraryLintFiles <desugar configuration> " - + "<desugar implementation> <output directory> [<android jar path for Android " + + "<desugar implementation> <output directory> <android jar path for Android " + MAX_TESTED_ANDROID_API_LEVEL - + " or higher>]")); + + " or higher>")); } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java index 5889b77..4bc0821 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
@@ -4,6 +4,11 @@ package com.android.tools.r8.ir.desugar.desugaredlibrary.lint; +import com.android.tools.r8.ArchiveClassFileProvider; +import com.android.tools.r8.ArchiveProgramResourceProvider; +import com.android.tools.r8.ClassFileResourceProvider; +import com.android.tools.r8.ProgramResourceProvider; +import com.android.tools.r8.StringResource; import com.android.tools.r8.graph.CfCode.LocalVariableInfo; import com.android.tools.r8.graph.ClassAccessFlags; import com.android.tools.r8.graph.DexEncodedField; @@ -19,9 +24,13 @@ import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; import java.io.PrintStream; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -37,12 +46,11 @@ private static final String SUP_4 = "<sup>4</sup>"; public GenerateHtmlDoc( - String desugarConfigurationPath, - String desugarImplementationPath, - String outputDirectory, - String androidJarPath) - throws Exception { - super(desugarConfigurationPath, desugarImplementationPath, outputDirectory, androidJarPath); + StringResource desugarSpecification, + Collection<ProgramResourceProvider> desugarImplementation, + Path outputDirectory, + Collection<ClassFileResourceProvider> androidJar) { + super(desugarSpecification, desugarImplementation, outputDirectory, androidJar); } private static class StringBuilderWithIndent { @@ -558,7 +566,7 @@ SupportedClasses supportedClasses = new SupportedClassesGenerator(options, androidJar) - .run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath); + .run(desugaredLibraryImplementation, desugaredLibrarySpecificationResource); // Full classes added. supportedClasses.forEachClass(supportedClass -> generateClassHTML(ps, supportedClass)); @@ -567,8 +575,13 @@ public static void main(String[] args) throws Exception { if (args[0].equals("--generate-api-docs")) { - if (args.length == 4 || args.length == 5) { - new GenerateHtmlDoc(args[1], args[2], args[3], getAndroidJarPath(args, 5)).run(); + if (args.length == 5) { + new GenerateHtmlDoc( + StringResource.fromFile(Paths.get(args[1])), + ImmutableList.of(ArchiveProgramResourceProvider.fromArchive(Paths.get(args[2]))), + Paths.get(args[3]), + ImmutableList.of(new ArchiveClassFileProvider(Paths.get(args[4])))) + .run(); return; } } @@ -576,8 +589,8 @@ StringUtils.joinLines( "Invalid invocation.", "Usage: GenerateHtmlDoc --generate-api-docs <desugar configuration> " - + "<desugar implementation> <output directory> [<android jar path for Android " + + "<desugar implementation> <output directory> <android jar path for Android " + MAX_TESTED_ANDROID_API_LEVEL - + " or higher>]")); + + " or higher>")); } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java index 12970fc..db8847b 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java
@@ -282,6 +282,10 @@ this.extraMethods = extraMethods; } + public boolean hasOnlyExtraMethods() { + return supportedClassBuilders.isEmpty(); + } + SupportedClasses build() { Map<DexType, SupportedClass> map = new IdentityHashMap<>(); supportedClassBuilders.forEach(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java index f79e61a..595092b 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
@@ -6,6 +6,8 @@ import static com.android.tools.r8.ir.desugar.desugaredlibrary.lint.AbstractGenerateFiles.MAX_TESTED_ANDROID_API_LEVEL; +import com.android.tools.r8.ClassFileResourceProvider; +import com.android.tools.r8.ProgramResourceProvider; import com.android.tools.r8.StringResource; import com.android.tools.r8.androidapi.ComputedApiLevel; import com.android.tools.r8.dex.ApplicationReader; @@ -44,7 +46,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import java.io.IOException; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -60,15 +61,17 @@ private final SupportedClasses.Builder builder = SupportedClasses.builder(); private final boolean addBackports; - public SupportedClassesGenerator(InternalOptions options, Path androidJar) throws IOException { - this.options = options; - this.appForMax = createAppForMax(androidJar); - this.minApi = AndroidApiLevel.B; - this.addBackports = false; + public SupportedClassesGenerator( + InternalOptions options, Collection<ClassFileResourceProvider> androidJar) + throws IOException { + this(options, androidJar, AndroidApiLevel.B, false); } public SupportedClassesGenerator( - InternalOptions options, Path androidJar, AndroidApiLevel minApi, boolean addBackports) + InternalOptions options, + Collection<ClassFileResourceProvider> androidJar, + AndroidApiLevel minApi, + boolean addBackports) throws IOException { this.options = options; this.appForMax = createAppForMax(androidJar); @@ -76,7 +79,9 @@ this.addBackports = addBackports; } - public SupportedClasses run(Collection<Path> desugaredLibraryImplementation, Path specification) + public SupportedClasses run( + Collection<ProgramResourceProvider> desugaredLibraryImplementation, + StringResource specification) throws IOException { // First analyze everything which is supported when desugaring for api 1. collectSupportedMembersInMinApi(desugaredLibraryImplementation, specification); @@ -128,7 +133,10 @@ return fullySupported; } - private void annotatePartialDesugaringMembers(Path specification) throws IOException { + private void annotatePartialDesugaringMembers(StringResource specification) throws IOException { + if (builder.hasOnlyExtraMethods()) { + return; + } // The first difference should be at 18 so we're safe starting at J and not B. for (int api = AndroidApiLevel.J.getLevel(); api <= MAX_TESTED_ANDROID_API_LEVEL.getLevel(); @@ -252,7 +260,9 @@ } private void collectSupportedMembersInMinApi( - Collection<Path> desugaredLibraryImplementation, Path specification) throws IOException { + Collection<ProgramResourceProvider> desugaredLibraryImplementation, + StringResource specification) + throws IOException { MachineDesugaredLibrarySpecification machineSpecification = getMachineSpecification(minApi, specification); @@ -261,8 +271,11 @@ options.resetDesugaredLibrarySpecificationForTesting(); options.setDesugaredLibrarySpecification(machineSpecification); - AndroidApp implementation = - AndroidApp.builder().addProgramFiles(desugaredLibraryImplementation).build(); + AndroidApp.Builder appBuilder = AndroidApp.builder(); + for (ProgramResourceProvider programResource : desugaredLibraryImplementation) { + appBuilder.addProgramResourceProvider(programResource); + } + AndroidApp implementation = appBuilder.build(); DirectMappedDexApplication implementationApplication = new ApplicationReader(implementation, options, Timing.empty()).read().toDirect(); @@ -305,7 +318,6 @@ } builder.addSupportedMethod(clazz, method); } - addBackports(clazz, backports); for (DexEncodedField field : clazz.fields()) { if (!field.isPublic() && !field.isProtected()) { continue; @@ -313,12 +325,13 @@ builder.addSupportedField(clazz, field); } } + addBackports(clazz, backports); } } // All retargeted methods are supported. machineSpecification.forEachRetargetMethod( - method -> registerMethod(method, implementationApplication)); + method -> registerMethod(method, implementationApplication, backports)); machineSpecification .getStaticFieldRetarget() @@ -353,7 +366,8 @@ } } - private void registerMethod(DexMethod method, DexApplication implementationApplication) { + private void registerMethod( + DexMethod method, DexApplication implementationApplication, List<DexMethod> backports) { DexClass dexClass = implementationApplication.definitionFor(method.getHolderType()); if (dexClass != null) { DexEncodedMethod dexEncodedMethod = dexClass.lookupMethod(method); @@ -368,6 +382,7 @@ if (dexEncodedMethod != null) { builder.addSupportedMethod(dexClass, dexEncodedMethod); builder.annotateClass(dexClass.getType(), ClassAnnotation.getAdditionnalMembersOnClass()); + backports.remove(method); } } @@ -425,20 +440,23 @@ } private MachineDesugaredLibrarySpecification getMachineSpecification( - AndroidApiLevel api, Path specification) throws IOException { + AndroidApiLevel api, StringResource specification) throws IOException { + if (specification == null) { + return MachineDesugaredLibrarySpecification.empty(); + } DesugaredLibrarySpecification librarySpecification = DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification( - StringResource.fromFile(specification), - options.itemFactory, - options.reporter, - false, - api.getLevel()); + specification, options.itemFactory, options.reporter, false, api.getLevel()); return librarySpecification.toMachineSpecification(appForMax, Timing.empty()); } - private DirectMappedDexApplication createAppForMax(Path androidJar) throws IOException { + private DirectMappedDexApplication createAppForMax( + Collection<ClassFileResourceProvider> androidJar) throws IOException { AndroidApp.Builder builder = AndroidApp.builder(); - AndroidApp inputApp = builder.addLibraryFiles(androidJar).build(); + for (ClassFileResourceProvider libraryResource : androidJar) { + builder.addLibraryResourceProvider(libraryResource); + } + AndroidApp inputApp = builder.build(); ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty()); ExecutorService executorService = ThreadUtils.getExecutorService(options); assert !options.ignoreJavaLibraryOverride;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java index 985eb29..51253df 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -89,6 +89,7 @@ return topLevelFlags.getSynthesizedLibraryClassesPackagePrefix(); } + @Override public String getIdentifier() { return topLevelFlags.getIdentifier(); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java index 21abec1..f151273 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -268,10 +268,12 @@ // Case (3), parameters that are not null after the invocation. BitSet nonNullParamOnNormalExits = optimizationInfo.getNonNullParamOnNormalExits(); if (nonNullParamOnNormalExits != null) { - int start = invoke.isInvokeMethodWithReceiver() ? 1 : 0; - for (int i = start; i < invoke.arguments().size(); i++) { - if (nonNullParamOnNormalExits.get(i)) { - Value argument = invoke.getArgument(i); + for (int argumentIndex = 0; argumentIndex < invoke.arguments().size(); argumentIndex++) { + boolean isArgumentNonNullOnNormalExits = + (invoke.isInvokeMethodWithReceiver() && argumentIndex == 0) + || nonNullParamOnNormalExits.get(argumentIndex); + if (isArgumentNonNullOnNormalExits) { + Value argument = invoke.getArgument(argumentIndex); if (assumedValuesBuilder.isMaybeNullAndNotNullType(argument) && isNullableReferenceTypeWithOtherNonDebugUsers(argument, invoke)) { assumedValuesBuilder.addNonNullValueWithUnknownDominance(invoke, argument);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java index f01fd1c..d0f24dd 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.ProgramField; +import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.analysis.value.AbstractValueFactory; import com.android.tools.r8.ir.analysis.value.SingleNumberValue; import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData; @@ -87,7 +88,7 @@ assert isUnboxedEnum(enumType); EnumData enumData = get(enumType); return isSuperUnboxedEnum(enumType) - ? enumData.superEnumTypeSingleValue(factory, enumType) + ? enumData.superEnumTypeSingleValue(factory) : enumData.subEnumTypeSingleValue(factory, enumType); } @@ -220,15 +221,15 @@ return valuesSize; } - public SingleNumberValue superEnumTypeSingleValue(AbstractValueFactory factory, DexType type) { + public SingleNumberValue superEnumTypeSingleValue(AbstractValueFactory factory) { // If there is a single live enum instance, then return the unboxed value for this one. if (hasValues()) { if (valuesSize == 1) { - return factory.createSingleNumberValue(ordinalToUnboxedInt(0)); + return factory.createSingleNumberValue(ordinalToUnboxedInt(0), TypeElement.getInt()); } } else if (unboxedValues.size() == 1) { Integer next = unboxedValues.values().iterator().next(); - return factory.createSingleNumberValue(ordinalToUnboxedInt(next)); + return factory.createSingleNumberValue(ordinalToUnboxedInt(next), TypeElement.getInt()); } return null; } @@ -238,7 +239,8 @@ assert valuesTypes.values().stream().filter(t -> t == type).count() <= 1; for (Entry<DexType> entry : valuesTypes.int2ReferenceEntrySet()) { if (entry.getValue() == type) { - return factory.createSingleNumberValue(ordinalToUnboxedInt(entry.getIntKey())); + return factory.createSingleNumberValue( + ordinalToUnboxedInt(entry.getIntKey()), TypeElement.getInt()); } } return null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java index a2639bf..20a8df6 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -798,7 +798,7 @@ @Override public void fixup(DexEncodedField field, MutableFieldOptimizationInfo optimizationInfo) { optimizationInfo - .fixupAbstractValue(appView, graphLens, codeLens) + .fixupAbstractValue(appView, field, graphLens, codeLens) .fixupClassTypeReferences(appView, graphLens); } @@ -820,7 +820,7 @@ } }) .fixupClassTypeReferences(appView, graphLens) - .fixupAbstractReturnValue(appView, graphLens, codeLens) + .fixupAbstractReturnValue(appView, method, graphLens, codeLens) .fixupInstanceInitializerInfo( appView, graphLens, codeLens, treeFixerResult.getPrunedItems()); @@ -1087,7 +1087,9 @@ enumClass, new MissingInstanceFieldValueForEnumInstanceReason(instanceField, ordinal)); return EnumInstanceFieldUnknownData.getInstance(); } - if (!(fieldValue.isSingleNumberValue() || fieldValue.isSingleStringValue())) { + if (!(fieldValue.isNull() + || fieldValue.isSingleNumberValue() + || fieldValue.isSingleStringValue())) { reportFailure( enumClass, new UnsupportedInstanceFieldValueForEnumInstanceReason(ordinal, instanceField));
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 8a5f087..95e4ea6 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
@@ -18,6 +18,7 @@ import com.android.tools.r8.graph.proto.ArgumentInfoCollection; import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription; import com.android.tools.r8.graph.proto.RewrittenTypeInfo; +import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.analysis.value.AbstractValueFactory; import com.android.tools.r8.ir.analysis.value.SingleFieldValue; @@ -212,7 +213,7 @@ SingleFieldValue singleFieldValue = singleValue.asSingleFieldValue(); if (unboxedEnums.hasUnboxedValueFor(singleFieldValue.getField())) { return abstractValueFactory.createSingleNumberValue( - unboxedEnums.getUnboxedValue(singleFieldValue.getField())); + unboxedEnums.getUnboxedValue(singleFieldValue.getField()), TypeElement.getInt()); } } return singleValue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java index 7449cee..ac0bc7b 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -34,7 +34,7 @@ import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.analysis.type.TypeElement; -import com.android.tools.r8.ir.analysis.value.SingleNumberValue; +import com.android.tools.r8.ir.analysis.value.SingleConstValue; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.ConstClass; import com.android.tools.r8.ir.code.IRCode; @@ -405,9 +405,9 @@ } for (ExtraParameter extraParameter : lookupResult.getPrototypeChanges().getExtraParameters()) { - SingleNumberValue singleNumberValue = extraParameter.getValue(appView); + SingleConstValue singleConstValue = extraParameter.getValue(appView); Instruction materializingInstruction = - singleNumberValue.createMaterializingInstruction( + singleConstValue.createMaterializingInstruction( appView, code, TypeAndLocalInfoSupplier.create(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java index 451acb2..01b4a7d 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -161,8 +161,8 @@ if (staticType.isArrayType()) { Nullability nullability = concreteParameterState.asArrayParameter().getNullability(); if (nullability.isDefinitelyNull()) { - newCallSiteInfo.constants.put( - argumentIndex, appView.abstractValueFactory().createNullValue()); + newCallSiteInfo.constants.put( + argumentIndex, appView.abstractValueFactory().createNullValue(staticType)); isTop = false; } else if (nullability.isDefinitelyNotNull()) { newCallSiteInfo.dynamicTypes.put(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/LibraryOptimizationInfoInitializerFeedback.java b/src/main/java/com/android/tools/r8/ir/optimize/info/LibraryOptimizationInfoInitializerFeedback.java index ace2aa6..90136f4 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/LibraryOptimizationInfoInitializerFeedback.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/LibraryOptimizationInfoInitializerFeedback.java
@@ -4,9 +4,6 @@ package com.android.tools.r8.ir.optimize.info; -import com.android.tools.r8.graph.DexEncodedField; -import com.android.tools.r8.ir.analysis.value.AbstractValue; - public class LibraryOptimizationInfoInitializerFeedback extends OptimizationFeedbackSimple { private static LibraryOptimizationInfoInitializerFeedback INSTANCE = @@ -17,9 +14,4 @@ public static LibraryOptimizationInfoInitializerFeedback getInstance() { return INSTANCE; } - - public void recordLibraryFieldHasAbstractValue( - DexEncodedField field, AbstractValue abstractValue) { - field.getMutableOptimizationInfo().setAbstractValue(abstractValue); - } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java index 6a2bc86..fadb852 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -32,6 +32,10 @@ return false; } + public boolean isMethodResolutionOptimizationInfo() { + return false; + } + public abstract boolean cannotBeKept(); public abstract boolean classInitializerMayBePostponed();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java index 3c06996..73e2edd 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -847,7 +847,9 @@ if (dynamicReturnType.isNullType()) { feedback.methodReturnsAbstractValue( - method.getDefinition(), appView, appView.abstractValueFactory().createNullValue()); + method.getDefinition(), + appView, + appView.abstractValueFactory().createNullValue(method.getReturnType())); feedback.setDynamicReturnType(method, appView, dynamicReturnType); return; } @@ -1015,7 +1017,7 @@ } List<Value> arguments = code.collectArguments(); BitSet paramsCheckedForNull = new BitSet(); - for (int index = 0; index < arguments.size(); index++) { + for (int index = method.getFirstNonReceiverArgumentIndex(); index < arguments.size(); index++) { Value argument = arguments.get(index); // This handles cases where the parameter is checked via Kotlin Intrinsics: // @@ -1055,7 +1057,9 @@ if (nonNullParamOrThrow != null) { facts.or(nonNullParamOrThrow); } - for (int index = 0; index < arguments.size(); index++) { + for (int index = code.context().getDefinition().getFirstNonReceiverArgumentIndex(); + index < arguments.size(); + index++) { if (facts.get(index)) { continue; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java new file mode 100644 index 0000000..2139f0f --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java
@@ -0,0 +1,326 @@ +// Copyright (c) 2023, 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.info; + +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint; +import com.android.tools.r8.ir.analysis.type.DynamicType; +import com.android.tools.r8.ir.analysis.value.AbstractValue; +import com.android.tools.r8.ir.code.InvokeDirect; +import com.android.tools.r8.ir.code.InvokeMethod; +import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint; +import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification; +import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo; +import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo; +import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.OptionalBool; +import java.util.BitSet; +import java.util.Set; +import java.util.function.Consumer; + +public class MethodResolutionOptimizationInfo extends MethodOptimizationInfo { + + private final AbstractValue abstractReturnValue; + private final DynamicType dynamicReturnType; + private final boolean mayHaveSideEffects; + private final boolean neverReturnsNormally; + private final BitSet nonNullParamOnNormalExits; + private final BitSet nonNullParamOrThrow; + private final int returnedArgument; + private final boolean returnValueOnlyDependsOnArguments; + + MethodResolutionOptimizationInfo( + AbstractValue abstractReturnValue, + DynamicType dynamicReturnType, + boolean mayHaveSideEffects, + boolean neverReturnsNormally, + BitSet nonNullParamOnNormalExits, + BitSet nonNullParamOrThrow, + int returnedArgument, + boolean returnValueOnlyDependsOnArguments) { + this.abstractReturnValue = abstractReturnValue; + this.dynamicReturnType = dynamicReturnType; + this.mayHaveSideEffects = mayHaveSideEffects; + this.neverReturnsNormally = neverReturnsNormally; + this.nonNullParamOnNormalExits = nonNullParamOnNormalExits; + this.nonNullParamOrThrow = nonNullParamOrThrow; + this.returnedArgument = returnedArgument; + this.returnValueOnlyDependsOnArguments = returnValueOnlyDependsOnArguments; + } + + static Builder builder() { + return new Builder(); + } + + static MethodOptimizationInfo create(MethodOptimizationInfo optimizationInfo) { + return builder() + .setAbstractReturnValue(optimizationInfo.getAbstractReturnValue()) + .setDynamicReturnType(optimizationInfo.getDynamicType()) + .setMayHaveSideEffects(optimizationInfo.mayHaveSideEffects()) + .setNeverReturnsNormally(optimizationInfo.neverReturnsNormally()) + .setNonNullParamOnNormalExits(optimizationInfo.getNonNullParamOnNormalExits()) + .setNonNullParamOrThrow(optimizationInfo.getNonNullParamOrThrow()) + .applyIf( + optimizationInfo.returnsArgument(), + builder -> builder.setReturnedArgument(optimizationInfo.getReturnedArgument())) + .setReturnValueOnlyDependsOnArguments(optimizationInfo.returnValueOnlyDependsOnArguments()) + .build(); + } + + @Override + public boolean isMethodResolutionOptimizationInfo() { + return true; + } + + @Override + public AbstractValue getAbstractReturnValue() { + return abstractReturnValue; + } + + @Override + public DynamicType getDynamicType() { + return dynamicReturnType; + } + + @Override + public boolean mayHaveSideEffects() { + return mayHaveSideEffects; + } + + @Override + public boolean mayHaveSideEffects(InvokeMethod invoke, InternalOptions options) { + return mayHaveSideEffects; + } + + @Override + public boolean neverReturnsNormally() { + return neverReturnsNormally; + } + + @Override + public BitSet getNonNullParamOnNormalExits() { + return nonNullParamOnNormalExits; + } + + @Override + public BitSet getNonNullParamOrThrow() { + return nonNullParamOrThrow; + } + + @Override + public boolean returnsArgument() { + return returnedArgument >= 0; + } + + @Override + public int getReturnedArgument() { + return returnedArgument; + } + + @Override + public boolean returnValueOnlyDependsOnArguments() { + return returnValueOnlyDependsOnArguments; + } + + @Override + public boolean hasBeenInlinedIntoSingleCallSite() { + throw new Unreachable(); + } + + @Override + public MutableMethodOptimizationInfo toMutableOptimizationInfo() { + throw new Unreachable(); + } + + @Override + public boolean cannotBeKept() { + throw new Unreachable(); + } + + @Override + public boolean classInitializerMayBePostponed() { + throw new Unreachable(); + } + + @Override + public CallSiteOptimizationInfo getArgumentInfos() { + throw new Unreachable(); + } + + @Override + public ClassInlinerMethodConstraint getClassInlinerMethodConstraint() { + throw new Unreachable(); + } + + @Override + public EnumUnboxerMethodClassification getEnumUnboxerMethodClassification() { + throw new Unreachable(); + } + + @Override + public int getMaxRemovedAndroidLogLevel() { + throw new Unreachable(); + } + + @Override + public BridgeInfo getBridgeInfo() { + throw new Unreachable(); + } + + @Override + public Set<DexType> getInitializedClassesOnNormalExit() { + throw new Unreachable(); + } + + @Override + public InstanceInitializerInfo getContextInsensitiveInstanceInitializerInfo() { + throw new Unreachable(); + } + + @Override + public InstanceInitializerInfo getInstanceInitializerInfo(InvokeDirect invoke) { + throw new Unreachable(); + } + + @Override + public boolean isConvertCheckNotNull() { + throw new Unreachable(); + } + + @Override + public boolean isInitializerEnablingJavaVmAssertions() { + throw new Unreachable(); + } + + @Override + public SimpleInliningConstraint getNopInliningConstraint(InternalOptions options) { + throw new Unreachable(); + } + + @Override + public SimpleInliningConstraint getSimpleInliningConstraint() { + throw new Unreachable(); + } + + @Override + public boolean hasParametersWithBitwiseOperations() { + throw new Unreachable(); + } + + @Override + public BitSet getParametersWithBitwiseOperations() { + throw new Unreachable(); + } + + @Override + public BitSet getUnusedArguments() { + throw new Unreachable(); + } + + @Override + public boolean isMultiCallerMethod() { + throw new Unreachable(); + } + + @Override + public OptionalBool isReturnValueUsed() { + throw new Unreachable(); + } + + @Override + public boolean forceInline() { + throw new Unreachable(); + } + + @Override + public boolean returnValueHasBeenPropagated() { + throw new Unreachable(); + } + + static class Builder { + + private AbstractValue abstractReturnValue = AbstractValue.unknown(); + private DynamicType dynamicReturnType = DynamicType.unknown(); + private boolean mayHaveSideEffects = true; + private boolean neverReturnsNormally = false; + private BitSet nonNullParamOnNormalExits = null; + private BitSet nonNullParamOrThrow = null; + private int returnedArgument = -1; + private boolean returnValueOnlyDependsOnArguments = false; + + Builder applyIf(boolean condition, Consumer<Builder> fn) { + if (condition) { + fn.accept(this); + } + return this; + } + + boolean isEffectivelyDefault() { + return dynamicReturnType.isUnknown() + && abstractReturnValue.isUnknown() + && returnedArgument < 0 + && nonNullParamOnNormalExits == null + && nonNullParamOrThrow == null + && mayHaveSideEffects + && !neverReturnsNormally + && !returnValueOnlyDependsOnArguments; + } + + Builder setAbstractReturnValue(AbstractValue abstractReturnValue) { + this.abstractReturnValue = abstractReturnValue; + return this; + } + + Builder setDynamicReturnType(DynamicType dynamicReturnType) { + this.dynamicReturnType = dynamicReturnType; + return this; + } + + Builder setMayHaveSideEffects(boolean mayHaveSideEffects) { + this.mayHaveSideEffects = mayHaveSideEffects; + return this; + } + + Builder setNeverReturnsNormally(boolean neverReturnsNormally) { + this.neverReturnsNormally = neverReturnsNormally; + return this; + } + + Builder setNonNullParamOnNormalExits(BitSet nonNullParamOnNormalExits) { + this.nonNullParamOnNormalExits = nonNullParamOnNormalExits; + return this; + } + + Builder setNonNullParamOrThrow(BitSet nonNullParamOrThrow) { + this.nonNullParamOrThrow = nonNullParamOrThrow; + return this; + } + + Builder setReturnedArgument(int returnedArgument) { + this.returnedArgument = returnedArgument; + return this; + } + + Builder setReturnValueOnlyDependsOnArguments(boolean returnValueOnlyDependsOnArguments) { + this.returnValueOnlyDependsOnArguments = returnValueOnlyDependsOnArguments; + return this; + } + + MethodOptimizationInfo build() { + if (isEffectivelyDefault()) { + return DefaultMethodOptimizationInfo.getInstance(); + } + return new MethodResolutionOptimizationInfo( + abstractReturnValue, + dynamicReturnType, + mayHaveSideEffects, + neverReturnsNormally, + nonNullParamOnNormalExits, + nonNullParamOrThrow, + returnedArgument, + returnValueOnlyDependsOnArguments); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoAnalysis.java new file mode 100644 index 0000000..e1b6f85 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoAnalysis.java
@@ -0,0 +1,409 @@ +// Copyright (c) 2023, 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.info; + +import static com.google.common.base.Predicates.not; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexMethodSignature; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo; +import com.android.tools.r8.graph.MethodResolutionResult; +import com.android.tools.r8.graph.ObjectAllocationInfoCollection; +import com.android.tools.r8.ir.analysis.type.DynamicType; +import com.android.tools.r8.ir.analysis.type.TypeElement; +import com.android.tools.r8.ir.analysis.value.AbstractValue; +import com.android.tools.r8.ir.conversion.PostMethodProcessor; +import com.android.tools.r8.optimize.argumentpropagation.utils.DepthFirstTopDownClassHierarchyTraversal; +import com.android.tools.r8.optimize.argumentpropagation.utils.ProgramClassesBidirectedGraph; +import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.shaking.KeepMethodInfo; +import com.android.tools.r8.utils.BitSetUtils; +import com.android.tools.r8.utils.MapUtils; +import com.android.tools.r8.utils.NumberUtils; +import com.android.tools.r8.utils.ThreadUtils; +import com.android.tools.r8.utils.collections.DexMethodSignatureMap; +import com.android.tools.r8.utils.collections.DexMethodSignatureSet; +import java.util.BitSet; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; + +public class MethodResolutionOptimizationInfoAnalysis { + + public static void run( + AppView<AppInfoWithLiveness> appView, + ExecutorService executorService, + PostMethodProcessor.Builder postMethodProcessorBuilder) + throws ExecutionException { + ImmediateProgramSubtypingInfo immediateSubtypingInfo = + ImmediateProgramSubtypingInfo.create(appView); + List<Set<DexProgramClass>> stronglyConnectedComponents = + new ProgramClassesBidirectedGraph(appView, immediateSubtypingInfo) + .computeStronglyConnectedComponents(); + MethodResolutionOptimizationInfoCollection.Builder builder = + MethodResolutionOptimizationInfoCollection.builder(); + ThreadUtils.processItems( + stronglyConnectedComponents, + stronglyConnectedComponent -> + new Traversal(appView, builder, immediateSubtypingInfo).run(stronglyConnectedComponent), + executorService); + MethodResolutionOptimizationInfoCollection methodResolutionOptimizationInfoCollection = + builder.build(); + appView.setMethodResolutionOptimizationInfoCollection( + methodResolutionOptimizationInfoCollection); + if (!methodResolutionOptimizationInfoCollection.isEmpty()) { + new MethodResolutionOptimizationInfoReprocessingEnqueuer(appView) + .enqueueMethodForReprocessing(executorService, postMethodProcessorBuilder); + } + } + + private static class Traversal extends DepthFirstTopDownClassHierarchyTraversal { + + private final MethodResolutionOptimizationInfoCollection.Builder builder; + private final Map<DexProgramClass, TraversalState> states = new IdentityHashMap<>(); + + private Traversal( + AppView<AppInfoWithLiveness> appView, + MethodResolutionOptimizationInfoCollection.Builder builder, + ImmediateProgramSubtypingInfo immediateSubtypingInfo) { + super(appView, immediateSubtypingInfo); + this.builder = builder; + } + + /** + * Build up a state that describes which virtual methods need to have their optimization info + * pushed upwards in the subsequent upwards traversal (instead of naively pushing optimization + * info upwards for *all* virtual methods). + * + * <p>Example: In the below piece of code, we want to push the optimization for C.m() up to + * class B, but we do not want to push the optimization info for B.m() and C.m() up to class A, + * since class A does not declare the method m(). + * + * <pre> + * class A {} + * class B extends A { + * void m() { ... } + * } + * class C extends B { + * void m() { ... } + * } + * </pre> + */ + @Override + public void visit(DexProgramClass clazz) { + DownwardsTraversalState state = new DownwardsTraversalState(DexMethodSignatureSet.create()); + immediateSubtypingInfo.forEachImmediateProgramSuperClass( + clazz, + superClass -> { + DownwardsTraversalState superState = + states + .getOrDefault(superClass, DownwardsTraversalState.empty()) + .asDownwardsTraversalState(); + state.add(superState); + for (DexEncodedMethod method : superClass.virtualMethods()) { + // No need to request optimization info for the (non-existing) overrides of final + // methods. + if (method.isFinal()) { + continue; + } + // If the method is not abstract and does not have any optimization info, there is no + // need to request the optimization info for overrides in subclasses, since the join + // of the optimization info becomes unknown anyway. + if (method.isAbstract() || !method.getOptimizationInfo().isDefault()) { + state.virtualMethodsInSuperClasses.add(method); + } + } + }); + if (!state.isEmpty()) { + states.put(clazz, state); + } + } + + @Override + public void prune(DexProgramClass clazz) { + // Extract the downwards traversal state that describes what info needs to be pushed upwards. + DownwardsTraversalState state = + MapUtils.removeOrDefault(states, clazz, DownwardsTraversalState.empty()) + .asDownwardsTraversalState(); + + // Join the optimization info for all methods in all immediate subclasses. + UpwardsTraversalState newState = new UpwardsTraversalState(DexMethodSignatureMap.create()); + forEachSubClass( + clazz, + subClass -> { + UpwardsTraversalState subClassState = + states + .getOrDefault(subClass, UpwardsTraversalState.empty()) + .asUpwardsTraversalState(); + newState.join(appView, subClassState); + + // If the current class is an interface and the current subclass is not, then we need + // special handling to account for the fact that invoke-interface instructions may + // dispatch to virtual "sibling" methods that are declared on a class that is not a + // subtype of the interface declaring the interface method. + if (clazz.isInterface() && !subClass.isInterface()) { + handleInvokeInterfaceToSiblingMethod(clazz, subClass, state, newState); + } + }); + ObjectAllocationInfoCollection objectAllocationInfoCollection = + appView.appInfo().getObjectAllocationInfoCollection(); + if (objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(clazz)) { + for (DexEncodedMethod method : clazz.virtualMethods()) { + newState.joinMethodOptimizationInfo( + appView, method.getSignature(), DefaultMethodOptimizationInfo.getInstance()); + } + } else { + for (DexEncodedMethod method : clazz.virtualMethods()) { + KeepMethodInfo keepInfo = appView.getKeepInfo().getMethodInfo(method, clazz); + if (!keepInfo.isShrinkingAllowed(appView.options())) { + // Method is kept and could be overridden outside app (e.g., in tests). Verify we don't + // have any optimization info recorded for non-abstract methods. + assert method.isAbstract() || method.getOptimizationInfo().isDefault(); + newState.joinMethodOptimizationInfo( + appView, method.getSignature(), DefaultMethodOptimizationInfo.getInstance()); + } else if (!method.isAbstract()) { + newState.joinMethodOptimizationInfo( + appView, method.getSignature(), method.getOptimizationInfo()); + } + } + } + + // Record the computed optimization info for all non-final virtual methods on the current + // class. + if (!clazz.isFinal()) { + for (DexEncodedMethod method : clazz.virtualMethods(not(DexEncodedMethod::isFinal))) { + MethodOptimizationInfo info = newState.getMethodOptimizationInfo(method); + assert info.isDefault() || info.isMethodResolutionOptimizationInfo(); + builder.add(method.getReference(), info); + } + } + + // Prune out everything that does not need to be pushed upwards and store the resulting state. + newState + .infos + .keySet() + .removeIf(method -> !state.virtualMethodsInSuperClasses.contains(method)); + if (!newState.isEmpty()) { + states.put(clazz, newState); + } + } + + private void handleInvokeInterfaceToSiblingMethod( + DexProgramClass iface, + DexProgramClass subClass, + DownwardsTraversalState state, + UpwardsTraversalState newState) { + assert iface.isInterface(); + assert !subClass.isInterface(); + + DexMethodSignatureSet interfaceMethodsInClassOrAbove = + DexMethodSignatureSet.create(state.virtualMethodsInSuperClasses); + interfaceMethodsInClassOrAbove.addAllMethods(iface.virtualMethods()); + + for (DexMethodSignature method : interfaceMethodsInClassOrAbove) { + MethodResolutionResult resolutionResult = + appView.appInfo().resolveMethodOnClass(subClass, method); + if (resolutionResult.isFailedResolution()) { + assert resolutionResult.asFailedResolution().hasMethodsCausingError(); + continue; + } + + if (resolutionResult.isMultiMethodResolutionResult()) { + // Conservatively drop the current optimization info. + newState.joinMethodOptimizationInfo( + appView, method, DefaultMethodOptimizationInfo.getInstance()); + continue; + } + + assert resolutionResult.isSingleResolution(); + DexClassAndMethod resolvedMethod = resolutionResult.getResolutionPair(); + if (!resolvedMethod.getHolder().isInterface() && resolvedMethod.getHolder() != subClass) { + newState.joinMethodOptimizationInfo( + appView, method, resolvedMethod.getOptimizationInfo()); + } + } + } + } + + private abstract static class TraversalState { + + DownwardsTraversalState asDownwardsTraversalState() { + return null; + } + + UpwardsTraversalState asUpwardsTraversalState() { + return null; + } + } + + // State that is used for each class during the downwards traversal over the class hierarchy. + private static class DownwardsTraversalState extends TraversalState { + + private static final DownwardsTraversalState EMPTY = + new DownwardsTraversalState(DexMethodSignatureSet.empty()); + + // The set of virtual methods in the super classes of the current class. For each method in this + // set we want the subsequent upwards traversal to include the optimization info for any + // overrides. + DexMethodSignatureSet virtualMethodsInSuperClasses; + + DownwardsTraversalState(DexMethodSignatureSet virtualMethodsInSuperClasses) { + this.virtualMethodsInSuperClasses = virtualMethodsInSuperClasses; + } + + static DownwardsTraversalState empty() { + return EMPTY; + } + + void add(DownwardsTraversalState state) { + virtualMethodsInSuperClasses.addAll(state.virtualMethodsInSuperClasses); + } + + boolean isEmpty() { + return virtualMethodsInSuperClasses.isEmpty(); + } + + @Override + DownwardsTraversalState asDownwardsTraversalState() { + return this; + } + } + + private static class UpwardsTraversalState extends TraversalState { + + private static final UpwardsTraversalState EMPTY = + new UpwardsTraversalState(DexMethodSignatureMap.empty()); + + private final DexMethodSignatureMap<MethodOptimizationInfo> infos; + + UpwardsTraversalState(DexMethodSignatureMap<MethodOptimizationInfo> infos) { + this.infos = infos; + } + + static UpwardsTraversalState empty() { + return EMPTY; + } + + void join(AppView<AppInfoWithLiveness> appView, UpwardsTraversalState state) { + state.infos.forEach((method, info) -> joinMethodOptimizationInfo(appView, method, info)); + } + + void joinMethodOptimizationInfo( + AppView<AppInfoWithLiveness> appView, + DexMethodSignature method, + MethodOptimizationInfo info) { + infos.compute( + method, + (m, existingInfo) -> existingInfo == null ? info : join(appView, m, existingInfo, info)); + } + + static MethodOptimizationInfo join( + AppView<AppInfoWithLiveness> appView, + DexMethodSignature method, + MethodOptimizationInfo info, + MethodOptimizationInfo otherInfo) { + if (info.isDefault() || otherInfo.isDefault()) { + return DefaultMethodOptimizationInfo.getInstance(); + } + + // Join dynamic return type. + DynamicType dynamicReturnType; + if (method.getReturnType().isVoidType()) { + dynamicReturnType = DynamicType.unknown(); + } else { + dynamicReturnType = info.getDynamicType().join(appView, otherInfo.getDynamicType()); + if (method.getReturnType().isClassType()) { + dynamicReturnType = + WideningUtils.widenDynamicNonReceiverType( + appView, dynamicReturnType, method.getReturnType()); + } else if (method.getReturnType().isArrayType()) { + // TODO: also widen array types. + } else { + assert method.getReturnType().isPrimitiveType(); + assert dynamicReturnType.isUnknown(); + } + } + + // Join abstract return value. + AbstractValue abstractReturnValue; + if (method.getReturnType().isVoidType()) { + abstractReturnValue = AbstractValue.unknown(); + } else { + TypeElement staticReturnType = method.getReturnType().toTypeElement(appView); + abstractReturnValue = + appView + .getAbstractValueConstantPropagationJoiner() + .join( + info.getAbstractReturnValue(), + otherInfo.getAbstractReturnValue(), + staticReturnType); + } + + // Join returned argument index. + int returnedArgument = + info.returnsArgument() && otherInfo.returnsArgument() + ? NumberUtils.getIfEqualsOrDefault( + info.getReturnedArgument(), otherInfo.getReturnedArgument(), -1) + : -1; + + // Join non-null-param-on-normal-exits information. + BitSet nonNullParamOnNormalExits = + BitSetUtils.intersectNullableBitSets( + info.getNonNullParamOnNormalExits(), otherInfo.getNonNullParamOnNormalExits()); + assert nonNullParamOnNormalExits == null || !nonNullParamOnNormalExits.isEmpty(); + + // Join non-null-param-or-throw information. + BitSet nonNullParamOrThrow = + BitSetUtils.intersectNullableBitSets( + info.getNonNullParamOrThrow(), otherInfo.getNonNullParamOrThrow()); + assert nonNullParamOrThrow == null || !nonNullParamOrThrow.isEmpty(); + + // Join flags. + boolean mayNotHaveSideEffects = !info.mayHaveSideEffects() && !otherInfo.mayHaveSideEffects(); + boolean neverReturnsNormally = + info.neverReturnsNormally() && otherInfo.neverReturnsNormally(); + boolean returnValueOnlyDependsOnArguments = + info.returnValueOnlyDependsOnArguments() && otherInfo.returnValueOnlyDependsOnArguments(); + + return MethodResolutionOptimizationInfo.builder() + .setAbstractReturnValue(abstractReturnValue) + .setDynamicReturnType(dynamicReturnType) + .setMayHaveSideEffects(!mayNotHaveSideEffects) + .setNeverReturnsNormally(neverReturnsNormally) + .setNonNullParamOnNormalExits(nonNullParamOnNormalExits) + .setNonNullParamOrThrow(nonNullParamOrThrow) + .setReturnedArgument(returnedArgument) + .setReturnValueOnlyDependsOnArguments(returnValueOnlyDependsOnArguments) + .build(); + } + + MethodOptimizationInfo getMethodOptimizationInfo(DexEncodedMethod method) { + MethodOptimizationInfo optimizationInfo = + infos.getOrDefault(method, DefaultMethodOptimizationInfo.getInstance()); + if (optimizationInfo.isMutableOptimizationInfo()) { + return MethodResolutionOptimizationInfo.create( + optimizationInfo.asMutableMethodOptimizationInfo()); + } + assert optimizationInfo.isDefault() || optimizationInfo.isMethodResolutionOptimizationInfo(); + return optimizationInfo; + } + + boolean isEmpty() { + return infos.isEmpty(); + } + + @Override + UpwardsTraversalState asUpwardsTraversalState() { + return this; + } + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoCollection.java index 6c2c13b..3ecca10 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoCollection.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoCollection.java
@@ -6,9 +6,11 @@ import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedMethod; -import com.android.tools.r8.graph.DexReference; +import com.android.tools.r8.graph.DexMethod; import java.util.Collections; +import java.util.IdentityHashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Collection of optimization info for virtual methods with dynamic dispatch. @@ -23,12 +25,16 @@ private static final MethodResolutionOptimizationInfoCollection EMPTY = new MethodResolutionOptimizationInfoCollection(Collections.emptyMap()); - private final Map<DexReference, MethodOptimizationInfo> backing; + private final Map<DexMethod, MethodOptimizationInfo> backing; - MethodResolutionOptimizationInfoCollection(Map<DexReference, MethodOptimizationInfo> backing) { + MethodResolutionOptimizationInfoCollection(Map<DexMethod, MethodOptimizationInfo> backing) { this.backing = backing; } + public static Builder builder() { + return new Builder(); + } + public static MethodResolutionOptimizationInfoCollection empty() { return EMPTY; } @@ -40,4 +46,24 @@ } return backing.getOrDefault(method.getReference(), defaultValue); } + + public boolean isEmpty() { + return backing.isEmpty(); + } + + public static class Builder { + + private final Map<DexMethod, MethodOptimizationInfo> backing = new ConcurrentHashMap<>(); + + void add(DexMethod method, MethodOptimizationInfo optimizationInfo) { + assert !backing.containsKey(method); + if (!optimizationInfo.isDefault()) { + backing.put(method, optimizationInfo); + } + } + + MethodResolutionOptimizationInfoCollection build() { + return new MethodResolutionOptimizationInfoCollection(new IdentityHashMap<>(backing)); + } + } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoReprocessingEnqueuer.java new file mode 100644 index 0000000..9ad09b2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoReprocessingEnqueuer.java
@@ -0,0 +1,164 @@ +// Copyright (c) 2023, 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.info; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexEncodedMethod; +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.graph.MethodResolutionResult.SingleResolutionResult; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.UseRegistryWithResult; +import com.android.tools.r8.graph.lens.GraphLens; +import com.android.tools.r8.ir.conversion.PostMethodProcessor; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.ThreadUtils; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; + +/** + * Finds the methods in the program that should be reprocessed due to non-trivial method resolution + * optimization info. + */ +class MethodResolutionOptimizationInfoReprocessingEnqueuer { + + private final AppView<AppInfoWithLiveness> appView; + + MethodResolutionOptimizationInfoReprocessingEnqueuer(AppView<AppInfoWithLiveness> appView) { + this.appView = appView; + } + + public void enqueueMethodForReprocessing( + ExecutorService executorService, PostMethodProcessor.Builder postMethodProcessorBuilder) + throws ExecutionException { + // Bring the methods to reprocess set up-to-date with the current graph lens. + postMethodProcessorBuilder.rewrittenWithLens(appView); + enqueueAffectedCallers(postMethodProcessorBuilder, executorService); + } + + private void enqueueAffectedCallers( + PostMethodProcessor.Builder postMethodProcessorBuilder, ExecutorService executorService) + throws ExecutionException { + GraphLens currentGraphLens = appView.graphLens(); + Collection<List<ProgramMethod>> methodsToReprocess = + ThreadUtils.processItemsWithResults( + appView.appInfo().classes(), + clazz -> { + List<ProgramMethod> methodsToReprocessInClass = new ArrayList<>(); + clazz.forEachProgramMethodMatching( + DexEncodedMethod::hasCode, + method -> { + if (!postMethodProcessorBuilder.contains(method, currentGraphLens) + && !appView.appInfo().isNeverReprocessMethod(method)) { + AffectedMethodUseRegistry registry = + new AffectedMethodUseRegistry(appView, method); + if (method.registerCodeReferencesWithResult(registry)) { + methodsToReprocessInClass.add(method); + } + } + }); + return methodsToReprocessInClass; + }, + executorService); + methodsToReprocess.forEach( + methodsToReprocessInClass -> + postMethodProcessorBuilder.addAll(methodsToReprocessInClass, currentGraphLens)); + } + + static class AffectedMethodUseRegistry extends UseRegistryWithResult<Boolean, ProgramMethod> { + + private final AppView<AppInfoWithLiveness> appViewWithLiveness; + + AffectedMethodUseRegistry( + AppView<AppInfoWithLiveness> appViewWithLiveness, ProgramMethod context) { + super(appViewWithLiveness, context, false); + this.appViewWithLiveness = appViewWithLiveness; + } + + private void markAffected() { + setResult(Boolean.TRUE); + } + + @Override + public void registerInvokeDirect(DexMethod method) { + DexMethod rewrittenMethod = + appViewWithLiveness.graphLens().lookupInvokeDirect(method, getContext()).getReference(); + registerInvokeMethod(rewrittenMethod); + } + + @Override + public void registerInvokeInterface(DexMethod method) { + DexMethod rewrittenMethod = + appViewWithLiveness + .graphLens() + .lookupInvokeInterface(method, getContext()) + .getReference(); + registerInvokeMethod(rewrittenMethod); + } + + @Override + public void registerInvokeStatic(DexMethod method) { + DexMethod rewrittenMethod = + appViewWithLiveness.graphLens().lookupInvokeStatic(method, getContext()).getReference(); + registerInvokeMethod(rewrittenMethod); + } + + @Override + public void registerInvokeSuper(DexMethod method) { + DexMethod rewrittenMethod = + appViewWithLiveness.graphLens().lookupInvokeSuper(method, getContext()).getReference(); + registerInvokeMethod(rewrittenMethod); + } + + @Override + public void registerInvokeVirtual(DexMethod method) { + DexMethod rewrittenMethod = + appViewWithLiveness.graphLens().lookupInvokeVirtual(method, getContext()).getReference(); + registerInvokeMethod(rewrittenMethod); + } + + private void registerInvokeMethod(DexMethod method) { + SingleResolutionResult<?> resolutionResult = + appViewWithLiveness + .appInfo() + .unsafeResolveMethodDueToDexFormatLegacy(method) + .asSingleResolution(); + if (resolutionResult == null) { + return; + } + + MethodResolutionOptimizationInfoCollection methodResolutionOptimizationInfoCollection = + appViewWithLiveness.getMethodResolutionOptimizationInfoCollection(); + MethodOptimizationInfo optimizationInfo = + methodResolutionOptimizationInfoCollection.get( + resolutionResult.getResolvedMethod(), resolutionResult.getResolvedHolder()); + if (!optimizationInfo.isDefault()) { + markAffected(); + } + } + + @Override + public void registerInstanceFieldRead(DexField field) {} + + @Override + public void registerInstanceFieldWrite(DexField field) {} + + @Override + public void registerStaticFieldRead(DexField field) {} + + @Override + public void registerStaticFieldWrite(DexField field) {} + + @Override + public void registerInitClass(DexType type) {} + + @Override + public void registerTypeReference(DexType type) {} + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java index 406aa24..4dc44df 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
@@ -7,6 +7,7 @@ import static java.util.Collections.emptySet; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.ir.analysis.type.DynamicType; @@ -59,15 +60,26 @@ return abstractValue; } - void setAbstractValue(AbstractValue abstractValue) { + MutableFieldOptimizationInfo setAbstractValue( + AbstractValue abstractValue, DexEncodedField field) { + assert !abstractValue.isNull() || field.getType().isReferenceType(); + assert !abstractValue.isSingleNumberValue() || field.getType().isPrimitiveType(); + return setAbstractValue(abstractValue); + } + + private MutableFieldOptimizationInfo setAbstractValue(AbstractValue abstractValue) { assert getAbstractValue().isUnknown() || abstractValue.isNonTrivial(); this.abstractValue = abstractValue; + return this; } public MutableFieldOptimizationInfo fixupAbstractValue( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { - setAbstractValue(abstractValue.rewrittenWithLens(appView, lens, codeLens)); - return this; + AppView<AppInfoWithLiveness> appView, + DexEncodedField field, + GraphLens lens, + GraphLens codeLens) { + return setAbstractValue( + abstractValue.rewrittenWithLens(appView, field.getType(), lens, codeLens), field); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java index 2f29795..167bbdc 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -13,7 +13,9 @@ import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.ir.analysis.inlining.NeverSimpleInliningConstraint; import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint; +import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.analysis.type.DynamicType; +import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.analysis.value.UnknownValue; @@ -138,7 +140,7 @@ initializedClassesOnNormalExit = template.initializedClassesOnNormalExit; returnedArgument = template.returnedArgument; abstractReturnValue = template.abstractReturnValue; - dynamicType = template.dynamicType; + setDynamicType(template.dynamicType); inlining = template.inlining; simpleInliningConstraint = template.simpleInliningConstraint; bridgeInfo = template.bridgeInfo; @@ -174,13 +176,38 @@ public MutableMethodOptimizationInfo fixupClassTypeReferences( AppView<AppInfoWithLiveness> appView, GraphLens lens, Set<DexType> prunedTypes) { - dynamicType = dynamicType.rewrittenWithLens(appView, lens, prunedTypes); - return this; + DynamicType rewrittenDynamicType = dynamicType.rewrittenWithLens(appView, lens, prunedTypes); + if (rewrittenDynamicType.hasDynamicUpperBoundType()) { + DynamicTypeWithUpperBound rewrittenDynamicTypeWithUpperBound = + rewrittenDynamicType.asDynamicTypeWithUpperBound(); + if (rewrittenDynamicTypeWithUpperBound.getDynamicUpperBoundType().isPrimitiveType()) { + // Do not store primitive dynamic types. + assert verifyDynamicTypeIsUnboxedEnum(appView, dynamicType); + return unsetDynamicType(); + } + } + return setDynamicType(rewrittenDynamicType); + } + + private static boolean verifyDynamicTypeIsUnboxedEnum( + AppView<?> appView, DynamicType dynamicType) { + assert dynamicType.isDynamicTypeWithUpperBound(); + DynamicTypeWithUpperBound dynamicTypeWithUpperBound = dynamicType.asDynamicTypeWithUpperBound(); + TypeElement dynamicUpperBoundType = dynamicTypeWithUpperBound.getDynamicUpperBoundType(); + assert dynamicUpperBoundType.isClassType(); + ClassTypeElement dynamicUpperBoundClassType = dynamicUpperBoundType.asClassType(); + assert appView.hasUnboxedEnums(); + assert appView.unboxedEnums().isUnboxedEnum(dynamicUpperBoundClassType.getClassType()); + return true; } public MutableMethodOptimizationInfo fixupAbstractReturnValue( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { - abstractReturnValue = abstractReturnValue.rewrittenWithLens(appView, lens, codeLens); + AppView<AppInfoWithLiveness> appView, + DexEncodedMethod method, + GraphLens lens, + GraphLens codeLens) { + abstractReturnValue = + abstractReturnValue.rewrittenWithLens(appView, method.getReturnType(), lens, codeLens); return this; } @@ -632,7 +659,13 @@ clearFlag(NEVER_RETURNS_NORMALLY_FLAG); } - void markReturnsAbstractValue(AbstractValue value) { + void setAbstractReturnValue(AbstractValue value, DexEncodedMethod method) { + assert !value.isNull() || method.getReturnType().isReferenceType(); + assert !value.isSingleNumberValue() || method.getReturnType().isPrimitiveType(); + setAbstractReturnValue(value); + } + + private void setAbstractReturnValue(AbstractValue value) { assert !abstractReturnValue.isSingleValue() || abstractReturnValue.equals(value) : "return single value changed from " + abstractReturnValue + " to " + value; abstractReturnValue = value; @@ -643,6 +676,11 @@ } void setDynamicType(AppView<?> appView, DynamicType newDynamicType, DexEncodedMethod method) { + setDynamicType(appView, newDynamicType, method.getReturnType().toTypeElement(appView)); + } + + public void setDynamicType( + AppView<?> appView, DynamicType newDynamicType, TypeElement staticReturnType) { assert newDynamicType != null; // We may get more precise type information if the method is reprocessed (e.g., due to // optimization info collected from all call sites), and hence the @@ -651,14 +689,20 @@ // Nullability could be less precise, though. For example, suppose a value is known to be // non-null after a safe invocation, hence recorded with the non-null variant. If that call is // inlined and the method is reprocessed, such non-null assumption cannot be made again. - assert verifyDynamicType(appView, newDynamicType, method); - dynamicType = newDynamicType; + assert verifyDynamicType(appView, newDynamicType, staticReturnType); + setDynamicType(newDynamicType); + } + + private MutableMethodOptimizationInfo setDynamicType(DynamicType dynamicType) { + assert !dynamicType.hasDynamicUpperBoundType() + || !dynamicType.asDynamicTypeWithUpperBound().getDynamicUpperBoundType().isPrimitiveType(); + this.dynamicType = dynamicType; + return this; } private boolean verifyDynamicType( - AppView<?> appView, DynamicType newDynamicType, DexEncodedMethod method) { + AppView<?> appView, DynamicType newDynamicType, TypeElement staticReturnType) { if (appView.enableWholeProgramOptimizations()) { - TypeElement staticReturnType = method.getReturnType().toTypeElement(appView); TypeElement previousDynamicUpperBoundType = dynamicType.getDynamicUpperBoundType(staticReturnType); TypeElement newDynamicUpperBoundType = @@ -673,8 +717,8 @@ return true; } - void unsetDynamicType() { - dynamicType = DynamicType.unknown(); + MutableMethodOptimizationInfo unsetDynamicType() { + return setDynamicType(DynamicType.unknown()); } // TODO(b/140214568): Should be package-private.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java index bbb1cec..5999efb 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -151,7 +151,7 @@ .get(field.getReference()) .hasReflectiveAccess(); if (appView.appInfo().mayPropagateValueFor(appView, field.getReference())) { - getFieldOptimizationInfoForUpdating(field).setAbstractValue(abstractValue); + getFieldOptimizationInfoForUpdating(field).setAbstractValue(abstractValue, field); } } @@ -188,7 +188,7 @@ public synchronized void methodReturnsAbstractValue( DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, AbstractValue value) { if (appView.appInfo().mayPropagateValueFor(appView, method.getReference())) { - getMethodOptimizationInfoForUpdating(method).markReturnsAbstractValue(value); + getMethodOptimizationInfoForUpdating(method).setAbstractReturnValue(value, method); } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java index c49c8be..a440900 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -65,7 +65,7 @@ public void recordFieldHasAbstractValue( DexEncodedField field, AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue) { if (appView.appInfo().mayPropagateValueFor(appView, field.getReference())) { - field.getMutableOptimizationInfo().setAbstractValue(abstractValue); + field.getMutableOptimizationInfo().setAbstractValue(abstractValue, field); } } @@ -111,7 +111,7 @@ @Override public void methodReturnsAbstractValue( DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, AbstractValue value) { - method.getMutableOptimizationInfo().markReturnsAbstractValue(value); + method.getMutableOptimizationInfo().setAbstractReturnValue(value, method); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java index d737cda..2683ff3 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.optimize.info.field; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.graph.proto.ArgumentInfo; import com.android.tools.r8.graph.proto.ArgumentInfoCollection; @@ -62,7 +63,7 @@ @Override public InstanceFieldInitializationInfo rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { // We don't have the context here to determine what should happen. It is the responsibility of // optimizations that change the proto of instance initializers to update the argument // initialization info.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java index 856dfc0..003eef8 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.optimize.info.field; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.graph.proto.ArgumentInfoCollection; import com.android.tools.r8.ir.analysis.value.SingleValue; @@ -51,5 +52,5 @@ ArgumentInfoCollection argumentInfoCollection); InstanceFieldInitializationInfo rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens); + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java index b095afc..0fecfb1 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.optimize.info.field; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.graph.proto.ArgumentInfoCollection; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; @@ -55,7 +56,7 @@ @Override public InstanceFieldInitializationInfo rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { EnumDataMap enumDataMap = appView.unboxedEnums(); if (dynamicLowerBoundType != null && enumDataMap.isUnboxedEnum(dynamicLowerBoundType.getClassType())) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java index 88a7626..7fa9d04 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
@@ -85,7 +85,7 @@ (field, info) -> builder.recordInitializationInfo( lens.lookupField(field, codeLens), - info.rewrittenWithLens(appView, lens, codeLens))); + info.rewrittenWithLens(appView, field.getType(), lens, codeLens))); return builder.build(); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java index 356926c..e4faa42 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.optimize.info.field; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.lens.GraphLens; import com.android.tools.r8.graph.proto.ArgumentInfoCollection; import com.android.tools.r8.shaking.AppInfoWithLiveness; @@ -37,7 +38,7 @@ @Override public InstanceFieldInitializationInfo rewrittenWithLens( - AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) { + AppView<AppInfoWithLiveness> appView, DexType newType, GraphLens lens, GraphLens codeLens) { return this; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java index a4ea5c9..87e57d7 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java
@@ -17,7 +17,6 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.analysis.value.SingleValue; -import com.android.tools.r8.ir.analysis.value.UnknownValue; import com.android.tools.r8.ir.code.ArrayGet; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.BasicBlockIterator; @@ -32,6 +31,7 @@ import com.android.tools.r8.ir.code.StaticGet; import com.android.tools.r8.ir.code.StaticPut; import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo; @@ -87,7 +87,7 @@ Instruction replacement = appView .abstractValueFactory() - .createNullValue() + .createNullValue(memberType) .createMaterializingInstruction(appView, code, arrayGet); affectedValues.addAll(arrayGet.outValue().affectedValues()); arrayGet.outValue().replaceUsers(replacement.outValue()); @@ -158,12 +158,12 @@ AbstractValue abstractReturnValue; if (invokedMethod.getReturnType().isAlwaysNull(appView)) { - abstractReturnValue = appView.abstractValueFactory().createNullValue(); - } else if (singleTarget != null) { abstractReturnValue = - singleTarget.getDefinition().getOptimizationInfo().getAbstractReturnValue(); + appView.abstractValueFactory().createNullValue(invokedMethod.getReturnType()); } else { - abstractReturnValue = UnknownValue.getInstance(); + MethodOptimizationInfo optimizationInfo = + resolutionResult.getOptimizationInfo(appView, invoke, singleTarget); + abstractReturnValue = optimizationInfo.getAbstractReturnValue(); } if (abstractReturnValue.isSingleValue()) { @@ -275,7 +275,7 @@ AbstractValue abstractValue; if (field.getType().isAlwaysNull(appView)) { - abstractValue = appView.abstractValueFactory().createSingleNumberValue(0); + abstractValue = appView.abstractValueFactory().createNullValue(field.getType()); } else if (appView.appInfo().isFieldWrittenByFieldPutInstruction(target)) { abstractValue = definition.getOptimizationInfo().getAbstractValue(); if (!definition.isStatic()) { @@ -299,7 +299,7 @@ assert verifyStaticFieldValueConsistentWithOptimizationInfo(appView, definition); } else { // This is guaranteed to read the default value of the field. - abstractValue = appView.abstractValueFactory().createSingleNumberValue(0); + abstractValue = appView.abstractValueFactory().createDefaultValue(field.getType()); } if (abstractValue.isSingleValue()) {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java index f60fe64..ca847d1 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
@@ -54,20 +54,19 @@ List<CfInstruction> instructions, AbstractValue value, DexType returnType) { // TODO(b/155368026): Support fields and const class fields. // Move this to something similar than SingleValue#createMaterializingInstruction - if (value.isSingleStringValue()) { + if (value.isNull()) { + assert returnType.isReferenceType(); + instructions.add(new CfConstNull()); + } else if (value.isSingleStringValue()) { assert returnType == appView.dexItemFactory().stringType; instructions.add(new CfConstString(value.asSingleStringValue().getDexString())); } else if (value.isSingleNumberValue()) { - if (returnType.isReferenceType()) { - assert value.isNull(); - instructions.add(new CfConstNull()); - } else { - instructions.add( - new CfConstNumber( - value.asSingleNumberValue().getValue(), ValueType.fromDexType(returnType))); - } + assert returnType.isPrimitiveType(); + instructions.add( + new CfConstNumber( + value.asSingleNumberValue().getValue(), ValueType.fromDexType(returnType))); } else { - throw new Unreachable("Only Number and String fields in enums are supported."); + throw new Unreachable("Unsupported value: " + value); } }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java index 5d67400..2c05e8f 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
@@ -68,7 +68,8 @@ private static KotlinAnnotationClassValueInfo create(KClassValue arg, DexItemFactory factory) { return new KotlinAnnotationClassValueInfo( - KotlinTypeReference.fromBinaryName(arg.getClassName(), factory, arg.getClassName()), + KotlinTypeReference.fromBinaryNameOrKotlinClassifier( + arg.getClassName(), factory, arg.getClassName()), arg.getArrayDimensionCount()); } @@ -98,7 +99,7 @@ private static KotlinAnnotationEnumValueInfo create(EnumValue arg, DexItemFactory factory) { return new KotlinAnnotationEnumValueInfo( - KotlinTypeReference.fromBinaryName( + KotlinTypeReference.fromBinaryNameOrKotlinClassifier( arg.getEnumClassName(), factory, arg.getEnumClassName()), arg.getEnumEntryName()); }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java index 759ef69..0904502 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
@@ -34,7 +34,7 @@ static KotlinAnnotationInfo create(KmAnnotation annotation, DexItemFactory factory) { return new KotlinAnnotationInfo( - KotlinTypeReference.fromBinaryName( + KotlinTypeReference.fromBinaryNameOrKotlinClassifier( annotation.getClassName(), factory, annotation.getClassName()), KotlinAnnotationArgumentInfo.create(annotation.getArguments(), factory)); }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java index 66c4f2e..8e60aa7 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -200,7 +200,7 @@ KmClass kmClass, DexItemFactory factory) { String anonymousObjectOriginName = JvmExtensionsKt.getAnonymousObjectOriginName(kmClass); if (anonymousObjectOriginName != null) { - return KotlinTypeReference.fromBinaryName( + return KotlinTypeReference.fromBinaryNameOrKotlinClassifier( anonymousObjectOriginName, factory, anonymousObjectOriginName); } return null; @@ -212,7 +212,8 @@ for (String nestedClass : nestedClasses) { String binaryName = clazz.type.toBinaryName() + DescriptorUtils.INNER_CLASS_SEPARATOR + nestedClass; - nestedTypes.add(KotlinTypeReference.fromBinaryName(binaryName, factory, nestedClass)); + nestedTypes.add( + KotlinTypeReference.fromBinaryNameOrKotlinClassifier(binaryName, factory, nestedClass)); } return nestedTypes.build(); } @@ -224,7 +225,9 @@ String binaryName = sealedSubClass.replace( DescriptorUtils.JAVA_PACKAGE_SEPARATOR, DescriptorUtils.INNER_CLASS_SEPARATOR); - sealedTypes.add(KotlinTypeReference.fromBinaryName(binaryName, factory, sealedSubClass)); + sealedTypes.add( + KotlinTypeReference.fromBinaryNameOrKotlinClassifier( + binaryName, factory, sealedSubClass)); } return sealedTypes.build(); }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java index 1e387ff..dc949fc 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -110,7 +110,7 @@ KmFunction kmFunction, DexItemFactory factory) { String lambdaClassOriginName = JvmExtensionsKt.getLambdaClassOriginName(kmFunction); if (lambdaClassOriginName != null) { - return KotlinTypeReference.fromBinaryName( + return KotlinTypeReference.fromBinaryNameOrKotlinClassifier( lambdaClassOriginName, factory, lambdaClassOriginName); } return null;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java index 9757ef4..0833a1c 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
@@ -40,7 +40,9 @@ DexItemFactory factory) { ImmutableList.Builder<KotlinTypeReference> builder = ImmutableList.builder(); for (String partClassName : kmMultiFileClassFacade.getPartClassNames()) { - builder.add(KotlinTypeReference.fromBinaryName(partClassName, factory, partClassName)); + builder.add( + KotlinTypeReference.fromBinaryNameOrKotlinClassifier( + partClassName, factory, partClassName)); } return new KotlinMultiFileClassFacadeInfo(builder.build(), packageName, metadataVersion); }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java index 553c174..700b508 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
@@ -25,15 +25,22 @@ private final DexType known; private final String originalName; + // A KotlinTypeReference may be either a Kotlin classifier or a binary name. + // We need to maintain the right type to avoid unnecessary difference between original and + // rewritten metadata. + private final boolean exportAsKotlinClassifier; - private KotlinTypeReference(String originalName, DexType known) { + private KotlinTypeReference( + String originalName, DexType known, boolean exportAsKotlinClassifier) { this.originalName = originalName; this.known = known; + this.exportAsKotlinClassifier = exportAsKotlinClassifier; assert known != null; } private KotlinTypeReference(String originalName) { this.known = null; + this.exportAsKotlinClassifier = false; this.originalName = originalName; assert originalName != null; } @@ -46,24 +53,40 @@ return originalName; } - static KotlinTypeReference fromBinaryName( - String binaryName, DexItemFactory factory, String originalName) { - if (DescriptorUtils.isValidBinaryName(binaryName)) { + static KotlinTypeReference fromBinaryNameOrKotlinClassifier( + String binaryNameOrKotlinClassifier, DexItemFactory factory, String originalName) { + // Kotlin classifiers are valid binary names. + if (DescriptorUtils.isValidBinaryName(binaryNameOrKotlinClassifier)) { + boolean interpretAsKotlinClassifier = + shouldBeInterpretedAsKotlinClassifier(binaryNameOrKotlinClassifier); return fromDescriptor( - DescriptorUtils.getDescriptorFromClassBinaryName(binaryName), factory, originalName); + interpretAsKotlinClassifier + ? DescriptorUtils.getDescriptorFromKotlinClassifier(binaryNameOrKotlinClassifier) + : DescriptorUtils.getDescriptorFromClassBinaryName(binaryNameOrKotlinClassifier), + factory, + originalName, + interpretAsKotlinClassifier); } - return new KotlinTypeReference(binaryName); + return new KotlinTypeReference(binaryNameOrKotlinClassifier); + } + + private static boolean shouldBeInterpretedAsKotlinClassifier( + String binaryNameOrKotlinClassifier) { + return binaryNameOrKotlinClassifier.contains("."); } static KotlinTypeReference fromDescriptor(String descriptor, DexItemFactory factory) { - return fromDescriptor(descriptor, factory, descriptor); + return fromDescriptor(descriptor, factory, descriptor, false); } static KotlinTypeReference fromDescriptor( - String descriptor, DexItemFactory factory, String originalName) { + String descriptor, + DexItemFactory factory, + String originalName, + boolean exportAsKotlinClassifier) { if (DescriptorUtils.isDescriptor(descriptor)) { DexType type = factory.createType(descriptor); - return new KotlinTypeReference(originalName, type); + return new KotlinTypeReference(originalName, type, exportAsKotlinClassifier); } return new KotlinTypeReference(originalName); } @@ -117,7 +140,11 @@ if (descriptor == null || descriptor.equals(defaultValue)) { rewrittenConsumer.accept(descriptor); } else { - rewrittenConsumer.accept(DescriptorUtils.getBinaryNameFromDescriptor(descriptor)); + String rewritten = + exportAsKotlinClassifier + ? DescriptorUtils.descriptorToKotlinClassifier(descriptor) + : DescriptorUtils.getBinaryNameFromDescriptor(descriptor); + rewrittenConsumer.accept(rewritten); } }, appView,
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java index a6f4321..877794a 100644 --- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java +++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -99,7 +99,6 @@ import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.code.Xor; import com.android.tools.r8.ir.conversion.ExtraParameter; -import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter; import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; import com.android.tools.r8.ir.conversion.StringSwitchConverter; import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload; @@ -328,7 +327,7 @@ for (ExtraParameter extraParameter : protoChanges.getExtraParameters()) { int newArgumentIndex = argumentsInfo.getNewArgumentIndex(index, numberOfRemovedArguments); DexType extraArgumentType = method.getArgumentType(newArgumentIndex); - if (extraParameter instanceof ExtraUnusedNullParameter) { + if (extraParameter.isUnused()) { // Note that we do *not* increment the index here as that would shift the SSA value map. addUnusedArgument(extraArgumentType); } else {
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 ef75e94..4f1544e 100644 --- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -384,7 +384,7 @@ ? MethodSignatureEquivalence.get() : MethodJavaSignatureEquivalence.get(); this.definitionEquivalence = - new Equivalence<DexEncodedMethod>() { + new Equivalence<>() { @Override protected boolean doEquivalent(DexEncodedMethod method, DexEncodedMethod other) { return equivalence.equivalent(method.getReference(), other.getReference());
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 64e6247..ccfeddc 100644 --- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -3,6 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.naming; +import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument; +import static com.android.tools.r8.utils.MapUtils.unmodifiableForTesting; + import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedMethod; @@ -18,13 +21,19 @@ import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; +import com.android.tools.r8.utils.WorkList; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -234,10 +243,9 @@ namingStates .getOrDefault(clazz.superType, rootNamingState) .createChild(reservationState)); - DexClass holder = appView.definitionFor(type); - if (holder != null && strategy.allowMemberRenaming(holder)) { - for (DexEncodedMethod method : holder.allMethodsSorted()) { - assignNameToMethod(holder, method, namingState); + if (strategy.allowMemberRenaming(clazz)) { + for (DexEncodedMethod method : clazz.allMethodsSorted()) { + assignNameToMethod(clazz, method, namingState); } } }); @@ -312,15 +320,76 @@ reservationStates.computeIfAbsent(frontier, ignore -> parent.createChild()); DexClass holder = appView.definitionFor(type); if (holder != null) { - for (DexEncodedMethod method : shuffleMethods(holder.methods(), appView.options())) { + // When javac compiles code against a class-path it will assume that the target of a synthetic + // bridge has the same name as the bridge. + // <pre> + // public class I { Result foo(); } + // public class Impl { + // ResultImpl foo() { ... } <-- this needs to be named foo + // Result {synthetic,bridge} foo() { invoke-virtual foo():()ResultImpl; } + // } + // </pre> + // In general we should consider the bridge and target as a group and reserve/rename as one + // however that is difficult in the current setup of reservation states. We therefore allow + // them to differ unless there is a kept/reserved method. For one to observe this one would + // have to do byte-code rewriting against a mapping file to observe the issue. Doing that they + // may as well just adjust the keep rules to keep the targets of bridges. + // See b/290711987 for an actual issue regarding this. + Set<DexEncodedMethod> bridgeMethodCandidates = Sets.newIdentityHashSet(); + Iterable<DexEncodedMethod> methods = shuffleMethods(holder.methods(), appView.options()); + for (DexEncodedMethod method : methods) { DexString reservedName = strategy.getReservedName(method, holder); if (reservedName != null) { state.reserveName(reservedName, method); + } else if (appView.options().isGeneratingClassFiles() && method.isSyntheticBridgeMethod()) { + bridgeMethodCandidates.add(method); + } + } + Map<DexString, Set<Integer>> methodNamesToReserve = + computeBridgesThatAreReserved(holder, bridgeMethodCandidates); + if (!methodNamesToReserve.isEmpty()) { + for (DexEncodedMethod method : methods) { + if (methodNamesToReserve + .getOrDefault(method.getName(), Collections.emptySet()) + .contains(method.getProto().getArity())) { + state.reserveName(method.getName(), method); + } } } } } + private Map<DexString, Set<Integer>> computeBridgesThatAreReserved( + DexClass holder, Set<DexEncodedMethod> methods) { + if (methods.isEmpty()) { + return Collections.emptyMap(); + } + WorkList<DexClass> workList = WorkList.newIdentityWorkList(holder); + Map<DexString, Set<Integer>> reservedNamesWithArity = new HashMap<>(); + while (workList.hasNext()) { + DexClass clazz = workList.next(); + MethodReservationState<?> state = reservationStates.get(frontiers.get(clazz.getType())); + if (state != null) { + methods.forEach( + bridgeMethod -> { + if (state.isReserved(bridgeMethod.getName(), bridgeMethod.getReference())) { + reservedNamesWithArity + .computeIfAbsent(bridgeMethod.getName(), ignoreArgument(HashSet::new)) + .add(bridgeMethod.getProto().getArity()); + } + }); + } + clazz.forEachImmediateSupertype( + superType -> { + DexClass superClass = appView.definitionFor(superType); + if (superClass != null) { + workList.addIfNotSeen(superClass); + } + }); + } + return unmodifiableForTesting(reservedNamesWithArity); + } + @SuppressWarnings("ReferenceEquality") private MethodNamingState<?> getOrAllocateMethodNamingStates(DexType type) { MethodNamingState<?> namingState = namingStates.get(type);
diff --git a/src/main/java/com/android/tools/r8/naming/MethodReservationState.java b/src/main/java/com/android/tools/r8/naming/MethodReservationState.java index b963b3f..61aed28 100644 --- a/src/main/java/com/android/tools/r8/naming/MethodReservationState.java +++ b/src/main/java/com/android/tools/r8/naming/MethodReservationState.java
@@ -98,8 +98,7 @@ originalToReservedNames = new HashMap<>(); reservedNames = new HashSet<>(); } - final Wrapper<DexMethod> wrapped = - MethodSignatureEquivalence.get().wrap(method.getReference()); + Wrapper<DexMethod> wrapped = MethodSignatureEquivalence.get().wrap(method.getReference()); originalToReservedNames.computeIfAbsent(wrapped, ignore -> new HashSet<>()).add(name); reservedNames.add(name); }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java index 4f97afa..12a71ce 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -135,7 +135,7 @@ @Override public void fixup( DexEncodedField field, MutableFieldOptimizationInfo optimizationInfo) { - optimizationInfo.fixupAbstractValue(appView, graphLens, codeLens); + optimizationInfo.fixupAbstractValue(appView, field, graphLens, codeLens); } @Override @@ -144,7 +144,7 @@ // Fixup the return value in case the method returns a field that had its signature // changed. optimizationInfo - .fixupAbstractReturnValue(appView, graphLens, codeLens) + .fixupAbstractReturnValue(appView, method, graphLens, codeLens) .fixupInstanceInitializerInfo(appView, graphLens, codeLens, prunedItems); // Rewrite the optimization info to account for method signature changes.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java index 0991b0f..1a94bc3 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -285,7 +285,8 @@ return parameterState; } return new ConcreteClassTypeParameterState( - appView.abstractValueFactory().createNullValue(), DynamicType.definitelyNull()); + appView.abstractValueFactory().createNullValue(argumentType), + DynamicType.definitelyNull()); }, null); return narrowedParameterStates != null
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java index aa25c14..fbce71a 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -30,6 +30,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.analysis.value.SingleValue; +import com.android.tools.r8.ir.conversion.ExtraUnusedIntParameter; import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo; @@ -457,7 +458,7 @@ } AbstractValue returnValueForMethod = method.getReturnType().isAlwaysNull(appView) - ? appView.abstractValueFactory().createNullValue() + ? appView.abstractValueFactory().createNullValue(method.getReturnType()) : method.getOptimizationInfo().getAbstractReturnValue(); if (!returnValueForMethod.isSingleValue() || !returnValueForMethod.asSingleValue().isMaterializableInAllContexts(appView) @@ -863,7 +864,10 @@ for (DexType extraArgumentType : ImmutableList.of(dexItemFactory.intType, dexItemFactory.objectType)) { RewrittenPrototypeDescription candidatePrototypeChanges = - prototypeChanges.withExtraParameters(new ExtraUnusedNullParameter(extraArgumentType)); + prototypeChanges.withExtraParameters( + extraArgumentType.isIntType() + ? new ExtraUnusedIntParameter() + : new ExtraUnusedNullParameter(extraArgumentType)); rewrittenMethod = candidatePrototypeChanges.rewriteMethod(method, dexItemFactory); if (instanceInitializerSignatures.add(rewrittenMethod)) { return candidatePrototypeChanges; @@ -1021,7 +1025,7 @@ private SingleValue getReturnValue(ProgramMethod method) { AbstractValue returnValue; if (method.getReturnType().isAlwaysNull(appView)) { - returnValue = appView.abstractValueFactory().createNullValue(); + returnValue = appView.abstractValueFactory().createNullValue(method.getReturnType()); } else if (method.getDefinition().belongsToVirtualPool() && returnValuesForVirtualMethods.containsKey(method)) { assert method.getAccessFlags().isAbstract();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeParameterState.java index 9a9de09..801ada2 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeParameterState.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeParameterState.java
@@ -50,7 +50,7 @@ @Override public AbstractValue getAbstractValue(AppView<AppInfoWithLiveness> appView) { if (getNullability().isDefinitelyNull()) { - return appView.abstractValueFactory().createNullValue(); + return appView.abstractValueFactory().createUncheckedNullValue(); } return AbstractValue.unknown(); }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java index c0dab86..93683aa 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
@@ -55,7 +55,7 @@ public AbstractValue getAbstractValue(AppView<AppInfoWithLiveness> appView) { if (getDynamicType().getNullability().isDefinitelyNull()) { assert abstractValue.isNull() || abstractValue.isUnknown(); - return appView.abstractValueFactory().createNullValue(); + return appView.abstractValueFactory().createUncheckedNullValue(); } return abstractValue; }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/DepthFirstTopDownClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/DepthFirstTopDownClassHierarchyTraversal.java index cc81166..a843a58 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/DepthFirstTopDownClassHierarchyTraversal.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/DepthFirstTopDownClassHierarchyTraversal.java
@@ -84,13 +84,12 @@ } public boolean isRoot(DexProgramClass clazz) { - DexType superType = clazz.getSuperType(); - if (superType == null) { - return true; - } - DexProgramClass superclass = asProgramClassOrNull(appView.definitionFor(superType)); - if (superclass != null) { - return false; + if (clazz.hasSuperType()) { + DexType superType = clazz.getSuperType(); + DexProgramClass superClass = asProgramClassOrNull(appView.definitionFor(superType)); + if (superClass != null) { + return false; + } } for (DexType implementedType : clazz.getInterfaces()) { DexProgramClass implementedClass = @@ -159,11 +158,11 @@ immediateSubtypingInfo.getSubclasses(clazz).forEach(consumer); } - private void processClass(DexProgramClass interfaceDefinition) { - assert !isClassSeenButNotFinished(interfaceDefinition); - assert !isClassFinished(interfaceDefinition); - visit(interfaceDefinition); - markSeenButNotFinished(interfaceDefinition); + private void processClass(DexProgramClass clazz) { + assert !isClassSeenButNotFinished(clazz); + assert !isClassFinished(clazz); + visit(clazz); + markSeenButNotFinished(clazz); } protected boolean isClassUnseen(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java index 9f7cbc7..726ab9d 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.analysis.type.DynamicType; import com.android.tools.r8.ir.analysis.type.Nullability; +import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.analysis.value.AbstractValueFactory; import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState; @@ -132,7 +133,8 @@ AbstractValueFactory abstractValueFactory = appView.abstractValueFactory(); switch (type) { case BOOLEAN: - return abstractValueFactory.createSingleNumberValue(BooleanUtils.intValue(booleanValue)); + return abstractValueFactory.createSingleNumberValue( + BooleanUtils.intValue(booleanValue), TypeElement.getBoolean()); case FIELD: DexClass holder = appView.definitionFor(fieldHolder); @@ -147,17 +149,17 @@ case NULLABILITY: return nullability.isDefinitelyNull() - ? abstractValueFactory.createNullValue() + ? abstractValueFactory.createUncheckedNullValue() : AbstractValue.unknown(); case VALUE_RANGE: if (valueType.isReferenceType()) { - return nullability.isDefinitelyNull() - ? abstractValueFactory.createNullValue() - : AbstractValue.unknown(); + assert false; + return AbstractValue.unknown(); } return longInterval.isSingleValue() - ? abstractValueFactory.createSingleNumberValue(longInterval.getSingleValue()) + ? abstractValueFactory.createSingleNumberValue( + longInterval.getSingleValue(), TypeElement.getLong()) : abstractValueFactory.createNumberFromIntervalValue( longInterval.getMin(), longInterval.getMax());
diff --git a/src/main/java/com/android/tools/r8/utils/BitSetUtils.java b/src/main/java/com/android/tools/r8/utils/BitSetUtils.java index 085a368..80e1790 100644 --- a/src/main/java/com/android/tools/r8/utils/BitSetUtils.java +++ b/src/main/java/com/android/tools/r8/utils/BitSetUtils.java
@@ -23,6 +23,16 @@ return newBitSet; } + // A null bit set is interpreted as the empty bit set. + public static BitSet intersectNullableBitSets(BitSet bitSet, BitSet other) { + if (bitSet == null || other == null) { + return null; + } + BitSet result = (BitSet) bitSet.clone(); + result.and(other); + return result.isEmpty() ? null : result; + } + public static boolean verifyLessThanOrEqualTo(BitSet bitSet, BitSet other) { assert other.equals(or(bitSet, other)); return true;
diff --git a/src/main/java/com/android/tools/r8/utils/MethodSignatureEquivalence.java b/src/main/java/com/android/tools/r8/utils/MethodSignatureEquivalence.java index 6a8d2c9..01f4031 100644 --- a/src/main/java/com/android/tools/r8/utils/MethodSignatureEquivalence.java +++ b/src/main/java/com/android/tools/r8/utils/MethodSignatureEquivalence.java
@@ -13,38 +13,21 @@ */ public class MethodSignatureEquivalence extends Equivalence<DexMethod> { - private static final MethodSignatureEquivalence INSTANCE = new MethodSignatureEquivalence(false); + private static final MethodSignatureEquivalence INSTANCE = new MethodSignatureEquivalence(); - private static final MethodSignatureEquivalence INSTANCE_IGNORE_NAME = - new MethodSignatureEquivalence(true); - - private final boolean ignoreName; - - private MethodSignatureEquivalence(boolean ignoreName) { - this.ignoreName = ignoreName; - } + private MethodSignatureEquivalence() {} public static MethodSignatureEquivalence get() { return INSTANCE; } - public static MethodSignatureEquivalence getEquivalenceIgnoreName() { - return INSTANCE_IGNORE_NAME; - } - @Override protected boolean doEquivalent(DexMethod a, DexMethod b) { - if (ignoreName) { - return a.proto.equals(b.proto); - } return a.name.equals(b.name) && a.proto.equals(b.proto); } @Override protected int doHash(DexMethod method) { - if (ignoreName) { - return method.proto.hashCode(); - } return method.name.hashCode() * 31 + method.proto.hashCode(); } }
diff --git a/src/main/java/com/android/tools/r8/utils/NumberUtils.java b/src/main/java/com/android/tools/r8/utils/NumberUtils.java index ca5dc22..ac1ee2c 100644 --- a/src/main/java/com/android/tools/r8/utils/NumberUtils.java +++ b/src/main/java/com/android/tools/r8/utils/NumberUtils.java
@@ -8,6 +8,13 @@ public class NumberUtils { + public static int getIfEqualsOrDefault(int value, int otherValue, int defaultValue) { + if (value == otherValue) { + return value; + } + return defaultValue; + } + public static boolean is4Bit(long value) { return Constants.S4BIT_MIN <= value && value <= Constants.S4BIT_MAX; }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java index bbf5afb..39377e5 100644 --- a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java +++ b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java
@@ -80,6 +80,10 @@ return backing.getOrDefault(key, defaultValue); } + public T getOrDefault(DexEncodedMethod method, T defaultValue) { + return getOrDefault(method.getSignature(), defaultValue); + } + @Override public void forEach(BiConsumer<? super DexMethodSignature, ? super T> action) { backing.forEach(action); @@ -170,6 +174,10 @@ return backing.get(key); } + public T get(DexEncodedMethod method) { + return get(method.getSignature()); + } + public boolean containsKey(DexMethodSignature signature) { return backing.containsKey(signature); }
diff --git a/src/main/keep.txt b/src/main/keep.txt index 89105b4..3e23b48 100644 --- a/src/main/keep.txt +++ b/src/main/keep.txt
@@ -33,22 +33,6 @@ # TODO(b/176783536): Avoid need to use -dontwarn. -include dontwarn.txt -# TODO(b/185756596): Remove when no longer needed --keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$FileFacade { - com.android.tools.r8.jetbrains.kotlin.Lazy packageData$delegate; -} --keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$Class { - com.android.tools.r8.jetbrains.kotlin.Lazy classData$delegate; -} --keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$SyntheticClass { - com.android.tools.r8.jetbrains.kotlin.Lazy functionData$delegate; -} --keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$MultiFileClassPart { - com.android.tools.r8.jetbrains.kotlin.Lazy packageData$delegate; -} --keep class com.android.tools.r8.jetbrains.kotlin.SafePublicationLazyImpl { - java.lang.Object getValue(); -} # Test in this class is using the class name to fing the original .java file. -keep class com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaringMethods
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java index 8cd7133..238594e 100644 --- a/src/test/java/com/android/tools/r8/R8TestBuilder.java +++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -425,8 +425,8 @@ public T enableAssumeNotNullAnnotations(String annotationPackageName) { return addInternalKeepRules( "-assumevalues class * {", - " @" + annotationPackageName + ".AssumeNotNull *** * return 1;", - " @" + annotationPackageName + ".AssumeNotNull *** *(...) return 1;", + " @" + annotationPackageName + ".AssumeNotNull *** * return _NONNULL_;", + " @" + annotationPackageName + ".AssumeNotNull *** *(...) return _NONNULL_;", "}"); }
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java index 276f815..f65ed07 100644 --- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -8,7 +8,6 @@ import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; @@ -657,15 +656,15 @@ ImmutableList.copyOf(jars), javaArgs.toArray(StringUtils.EMPTY_ARRAY)); assertEquals("JVM run failed", javaResult.exitCode, 0); - assertTrue( - "JVM output does not match art output.\n\tjvm: " - + javaResult.stdout - + "\n\tart: " - + output, - output.replace("\r", "").equals(javaResult.stdout.replace("\r", ""))); + assertEquals( + "JVM output does not match art output.", + javaResult.stdout.replace("\r", ""), + output.replace("\r", "")); } } catch (Throwable t) { - assertTrue("Test was not expected to fail. Failed with " + t.getMessage(), expectedToFail); + if (!expectedToFail) { + throw new AssertionError("Test was not expected to fail", t); + } } }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java index d902754..8cacb48 100644 --- a/src/test/java/com/android/tools/r8/ToolHelper.java +++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -70,6 +70,7 @@ import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Arrays; @@ -82,6 +83,7 @@ import java.util.ListIterator; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.function.Consumer; @@ -752,6 +754,61 @@ } } + public static class CommandCacheStatistics { + + public static CommandCacheStatistics INSTANCE = new CommandCacheStatistics(); + private final Path cachePutCounter; + private final Path cacheMissCounter; + private final Path cacheHitCounter; + + private CommandCacheStatistics() { + String commandCacheStatsDir = System.getProperty("command_cache_stats_dir"); + if (commandCacheStatsDir != null) { + String processSpecificUUID = UUID.randomUUID().toString(); + cachePutCounter = Paths.get(commandCacheStatsDir, processSpecificUUID + "CACHEPUT"); + cacheMissCounter = Paths.get(commandCacheStatsDir, processSpecificUUID + "CACHEFAIL"); + cacheHitCounter = Paths.get(commandCacheStatsDir, processSpecificUUID + "CACHEHIT"); + try { + Files.createFile(cachePutCounter); + Files.createFile(cacheMissCounter); + Files.createFile(cacheHitCounter); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + cachePutCounter = null; + cacheMissCounter = null; + cacheHitCounter = null; + } + } + + private static void increaseCount(Path path) { + // Not enabled + if (path == null) { + return; + } + synchronized (path) { + try { + Files.write(path, "X".getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + public void addCachePut() { + increaseCount(cachePutCounter); + } + + public void addCacheHit() { + increaseCount(cacheHitCounter); + } + + public void addCacheMiss() { + increaseCount(cacheMissCounter); + } + } + public static class CommandResultCache { private static CommandResultCache INSTANCE = System.getProperty("command_cache_dir") != null @@ -789,7 +846,7 @@ } private Path getTempFile(Path path) { - return Paths.get(path.toString() + ".temp" + Thread.currentThread().getId()); + return Paths.get(path.toString() + ".temp" + UUID.randomUUID()); } private String getStringContent(Path path) { @@ -815,6 +872,7 @@ // but this should have no impact. Path outputFile = getOutputFile(cacheLookupKey); + CommandCacheStatistics.INSTANCE.addCacheHit(); return new Pair( new ProcessResult( exitCode, @@ -822,23 +880,25 @@ getStringContent(getStderrFile(cacheLookupKey))), outputFile.toFile().exists() ? outputFile : null); } + CommandCacheStatistics.INSTANCE.addCacheMiss(); return null; } public void putResult(ProcessResult result, CacheLookupKey cacheLookupKey, Path output) { + Path exitCodeFile = getExitCodeFile(cacheLookupKey); + Path exitCodeTempFile = getTempFile(exitCodeFile); + Path stdoutFile = getStdoutFile(cacheLookupKey); + Path stdoutTempFile = getTempFile(stdoutFile); + Path stderrFile = getStderrFile(cacheLookupKey); + Path stderrTempFile = getTempFile(stderrFile); + Path outputFile = getOutputFile(cacheLookupKey); + Path outputTempFile = getTempFile(outputFile); + try { String exitCode = "" + result.exitCode; // We avoid race conditions of writing vs reading by first writing all 3 files to temp // files, then moving these to the result files, moving last the exitcode file (which is // what we use as cache present check) - Path exitCodeFile = getExitCodeFile(cacheLookupKey); - Path exitCodeTempFile = getTempFile(exitCodeFile); - Path stdoutFile = getStdoutFile(cacheLookupKey); - Path stdoutTempFile = getTempFile(stdoutFile); - Path stderrFile = getStderrFile(cacheLookupKey); - Path stderrTempFile = getTempFile(stderrFile); - Path outputfile = getOutputFile(cacheLookupKey); - Path outputTempFile = getTempFile(outputfile); Files.write(exitCodeTempFile, exitCode.getBytes(StandardCharsets.UTF_8)); Files.write(stdoutTempFile, result.stdout.getBytes(StandardCharsets.UTF_8)); Files.write(stderrTempFile, result.stderr.getBytes(StandardCharsets.UTF_8)); @@ -859,7 +919,7 @@ if (output != null) { Files.move( outputTempFile, - outputfile, + outputFile, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); } @@ -868,8 +928,15 @@ exitCodeFile, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); + CommandCacheStatistics.INSTANCE.addCachePut(); } catch (IOException e) { - throw new RuntimeException(e); + StringBuilder exceptionMessage = new StringBuilder(); + exceptionMessage.append( + "Files.exists(exitCodeTempFile) = " + Files.exists(exitCodeTempFile)); + exceptionMessage.append("Files.exists(stdoutTempFile) = " + Files.exists(stdoutTempFile)); + exceptionMessage.append("Files.exists(stderrTempFile) = " + Files.exists(stderrTempFile)); + exceptionMessage.append("Files.exists(outputTempFile) = " + Files.exists(outputTempFile)); + throw new RuntimeException(exceptionMessage.toString(), e); } } } @@ -1281,7 +1348,7 @@ public static TemporaryFolder getTemporaryFolderForTest() { String tmpDir = System.getProperty("test_dir"); if (tmpDir == null) { - return new TemporaryFolder(ToolHelper.isLinux() ? null : Paths.get("build", "tmp").toFile()); + return new TemporaryFolder(); } else { return new RetainedTemporaryFolder(new java.io.File(tmpDir)); }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BigDecimalBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BigDecimalBackportTest.java new file mode 100644 index 0000000..edb29f1 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/backports/BigDecimalBackportTest.java
@@ -0,0 +1,39 @@ +// Copyright (c) 2023, 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.desugar.backports; + +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.utils.AndroidApiLevel; +import java.math.BigDecimal; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public final class BigDecimalBackportTest extends AbstractBackportTest { + @Parameters(name = "{0}") + public static Iterable<?> data() { + return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); + } + + public BigDecimalBackportTest(TestParameters parameters) { + super(parameters, BigDecimal.class, Main.class); + registerTarget(AndroidApiLevel.Q, 3); + ignoreInvokes("valueOf"); + ignoreInvokes("toString"); + } + + static final class Main extends MiniAssert { + public static void main(String[] args) { + testStripTrailingZeros(); + } + + private static void testStripTrailingZeros() { + assertEquals("0", BigDecimal.valueOf(0.0).stripTrailingZeros().toString()); + assertEquals("1", BigDecimal.valueOf(1.0).stripTrailingZeros().toString()); + assertEquals("-1", BigDecimal.valueOf(-1.0).stripTrailingZeros().toString()); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java index 1a744bb..1071a87 100644 --- a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java +++ b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
@@ -26,6 +26,7 @@ import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.MethodSubject; import com.google.common.collect.ImmutableSet; +import java.math.BigDecimal; import java.util.Arrays; import java.util.List; import java.util.Set; @@ -70,10 +71,17 @@ "compareAndSet", int.class, Object.class, Object.class)); assert AtomicReference_compareAndSet.getReturnType().getTypeName().equals("boolean"); + MethodReference BigDecimal_stripTrailingZeros = + Reference.methodFromMethod(BigDecimal.class.getDeclaredMethod("stripTrailingZeros")); + assert BigDecimal_stripTrailingZeros.getReturnType() + .getTypeName() + .equals("java.math.BigDecimal"); + return ImmutableSet.of( factory.createMethod(AtomicReferenceFieldUpdater_compareAndSet), factory.createMethod(AtomicReference_compareAndSet), - factory.createMethod(AtomicReferenceArray_compareAndSet)); + factory.createMethod(AtomicReferenceArray_compareAndSet), + factory.createMethod(BigDecimal_stripTrailingZeros)); } @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredMethodsListTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredMethodsListTest.java index 451d62c..25494ca 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredMethodsListTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredMethodsListTest.java
@@ -11,17 +11,25 @@ import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.StringResource; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification; +import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification; +import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser; import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.DesugaredMethodsList; +import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.GenerateDesugaredLibraryLintFiles; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.FileUtils; +import com.android.tools.r8.utils.InternalOptions; import com.google.common.collect.ImmutableList; import java.nio.file.Path; import java.util.ArrayList; import java.util.Comparator; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -52,24 +60,38 @@ return lintContents.contains(type); } - private void checkFileContent(AndroidApiLevel minApiLevel, Path lintFile) throws Exception { + private void checkFileContent( + AndroidApiLevel minApiLevel, + Path lintFile, + boolean includeBackports, + boolean includeDesugaredLibrary) + throws Exception { // Just do some light probing in the generated lint files. lintContents = FileUtils.readAllLines(lintFile); // All methods supported on BiFunction with maintain prefix. assertEquals( - minApiLevel.isLessThan(AndroidApiLevel.N), + minApiLevel.isLessThan(AndroidApiLevel.N) && includeDesugaredLibrary, supportsAllMethodsOf("java/util/function/BiFunction")); assertEquals( minApiLevel.isLessThan(AndroidApiLevel.O) - && libraryDesugaringSpecification != JDK11_MINIMAL, + && libraryDesugaringSpecification != JDK11_MINIMAL + && includeDesugaredLibrary, supportsAllMethodsOf("java/time/MonthDay")); + assertEquals( + minApiLevel.isLessThan(AndroidApiLevel.O) && includeBackports, + lintContents.contains("java/lang/Byte#toUnsignedInt(B)I")); + // File should be sorted. List<String> sorted = new ArrayList<>(lintContents); sorted.sort(Comparator.naturalOrder()); assertEquals(lintContents, sorted); + + // No duplicates. + Set<String> set = new HashSet<>(lintContents); + assertEquals(set.size(), lintContents.size()); } @Test @@ -80,6 +102,7 @@ ? ToolHelper.DESUGARED_JDK_8_LIB_JAR : LibraryDesugaringSpecification.getTempLibraryJDK11Undesugar(); + // TODO(b/b/302508099): Refactor users of parameters.getRuntime().asDex().getMinApiLevel(). AndroidApiLevel minApi = parameters.getRuntime().asDex().getMinApiLevel(); DesugaredMethodsList.main( new String[] { @@ -89,6 +112,54 @@ output.toString(), ToolHelper.getAndroidJar(AndroidApiLevel.U).toString() }); - checkFileContent(minApi, output); + checkFileContent(minApi, output, true, true); + } + + @Test + public void testDesugaredLibLint() throws Exception { + Assume.assumeTrue( + "Run for a single api because the test is independent of api", + parameters.getRuntime().asDex().getMinApiLevel().isEqualTo(AndroidApiLevel.T)); + Path output = temp.newFolder("lint").toPath(); + Path jdkLibJar = + libraryDesugaringSpecification == JDK8 + ? ToolHelper.DESUGARED_JDK_8_LIB_JAR + : LibraryDesugaringSpecification.getTempLibraryJDK11Undesugar(); + + GenerateDesugaredLibraryLintFiles.main( + new String[] { + libraryDesugaringSpecification.getSpecification().toString(), + jdkLibJar.toString(), + output.toString(), + ToolHelper.getAndroidJar(AndroidApiLevel.U).toString() + }); + + InternalOptions options = new InternalOptions(); + DesugaredLibrarySpecification spec = + DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification( + StringResource.fromFile(libraryDesugaringSpecification.getSpecification()), + options.dexItemFactory(), + options.reporter, + true, + 1); + int req = spec.getRequiredCompilationApiLevel().getLevel(); + Path file = output.resolve("compile_api_level_" + req + "/desugared_apis_" + req + "_1.txt"); + checkFileContent(AndroidApiLevel.B, file, false, true); + } + + @Test + public void testBackportedMethodLint() throws Exception { + Path output = temp.newFile("backport.txt").toPath(); + // TODO(b/b/302508099): Refactor users of parameters.getRuntime().asDex().getMinApiLevel(). + AndroidApiLevel minApi = parameters.getRuntime().asDex().getMinApiLevel(); + DesugaredMethodsList.main( + new String[] { + String.valueOf(minApi.getLevel()), + null, + null, + output.toString(), + ToolHelper.getAndroidJar(AndroidApiLevel.U).toString() + }); + checkFileContent(minApi, output, true, false); } }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java index 2ccc573..87d5720 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -25,7 +25,6 @@ import com.android.tools.r8.graph.GenericSignature.TypeSignature; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser; -import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.GenerateDesugaredLibraryLintFiles; import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification; import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.WrapperDescriptor; import com.android.tools.r8.naming.MemberNaming.MethodSignature; @@ -501,20 +500,6 @@ return existing; } - private CodeInspector getDesugaredApiJar() throws Exception { - Path out = temp.newFolder().toPath(); - GenerateDesugaredLibraryLintFiles desugaredApi = - GenerateDesugaredLibraryLintFiles.createForTesting( - libraryDesugaringSpecification.getSpecification(), - libraryDesugaringSpecification.getDesugarJdkLibs(), - out, - ToolHelper.getAndroidJar(AndroidApiLevel.U)); - AndroidApiLevel compileApi = desugaredApi.run(); - return new CodeInspector( - out.resolve("compile_api_level_" + compileApi.getLevel()) - .resolve("desugared_apis_" + compileApi.getLevel() + "_" + minApi.getLevel() + ".jar")); - } - private boolean addType( Consumer<ClassReference> additions, String type,
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java index ce9ae53..e07b779 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -13,6 +13,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.ArchiveClassFileProvider; +import com.android.tools.r8.ArchiveProgramResourceProvider; import com.android.tools.r8.StringResource; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; @@ -249,18 +251,18 @@ ? ToolHelper.DESUGARED_JDK_8_LIB_JAR : LibraryDesugaringSpecification.getTempLibraryJDK11Undesugar(); new GenerateHtmlDoc( - spec.getSpecification().toString(), - jdkLibJar.toString(), - html.toString(), - ANDROID_JAR_34) + StringResource.fromFile(spec.getSpecification()), + ImmutableList.of(ArchiveProgramResourceProvider.fromArchive(jdkLibJar)), + html, + ImmutableList.of(new ArchiveClassFileProvider(Paths.get(ANDROID_JAR_34)))) .run(spec + ".html"); Path lint = top.resolve("lint_" + spec); Files.createDirectories(lint); new GenerateDesugaredLibraryLintFiles( - spec.getSpecification().toString(), - jdkLibJar.toString(), - lint.toString(), - ANDROID_JAR_34) + StringResource.fromFile(spec.getSpecification()), + ImmutableList.of(ArchiveProgramResourceProvider.fromArchive(jdkLibJar)), + lint, + ImmutableList.of(new ArchiveClassFileProvider(Paths.get(ANDROID_JAR_34)))) .run(); } }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java index 18726787..74ac232 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
@@ -11,6 +11,10 @@ import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8AndAll3Jdk11; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.ArchiveClassFileProvider; +import com.android.tools.r8.ArchiveProgramResourceProvider; +import com.android.tools.r8.ProgramResourceProvider; +import com.android.tools.r8.StringResource; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification; @@ -133,9 +137,17 @@ options .getArtProfileOptions() .setAllowReadingEmptyArtProfileProvidersMultipleTimesForTesting(true); + Set<ProgramResourceProvider> programResources = + librarySpecification.getDesugarJdkLibs().stream() + .map(ArchiveProgramResourceProvider::fromArchive) + .collect(Collectors.toSet()); SupportedClasses supportedClasses = - new SupportedClassesGenerator(options, ToolHelper.getAndroidJar(AndroidApiLevel.U)) - .run(librarySpecification.getDesugarJdkLibs(), librarySpecification.getSpecification()); + new SupportedClassesGenerator( + options, + ImmutableList.of( + new ArchiveClassFileProvider(ToolHelper.getAndroidJar(AndroidApiLevel.U)))) + .run( + programResources, StringResource.fromFile(librarySpecification.getSpecification())); for (AndroidApiLevel api : getRelevantApiLevels()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java b/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java index c7d877e..ddd0285 100644 --- a/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java +++ b/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java
@@ -27,10 +27,8 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - // TODO(b/218293990): Right now the JDK 20 tests are built with -target 17, as our Gradle - // version does not know of -target 20. return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK17) + .withCfRuntimesStartingFromIncluding(CfVm.JDK20) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .build();
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 faf304f..7cd720a 100644 --- a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java +++ b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
@@ -135,13 +135,13 @@ 1, RemovedArgumentInfo.builder() .setType(factory.intType) - .setSingleValue(abstractValueFactory.createNullValue()) + .setSingleValue(abstractValueFactory.createZeroValue()) .build()); builder1.addArgumentInfo( 3, RemovedArgumentInfo.builder() .setType(factory.intType) - .setSingleValue(abstractValueFactory.createNullValue()) + .setSingleValue(abstractValueFactory.createZeroValue()) .build()); ArgumentInfoCollection arguments1 = builder1.setArgumentInfosSize(5).build();
diff --git a/src/test/java/com/android/tools/r8/internal/ChromeCompilationBase.java b/src/test/java/com/android/tools/r8/internal/ChromeCompilationBase.java index b80b932..60dcfdd 100644 --- a/src/test/java/com/android/tools/r8/internal/ChromeCompilationBase.java +++ b/src/test/java/com/android/tools/r8/internal/ChromeCompilationBase.java
@@ -7,6 +7,7 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; +import com.android.tools.r8.ToolHelper; import com.android.tools.r8.utils.FileUtils; import com.google.common.collect.ImmutableList; import java.io.IOException; @@ -25,7 +26,8 @@ public ChromeCompilationBase(int version, boolean minimal) { this.base = - "third_party/chrome/" + ToolHelper.THIRD_PARTY_DIR + + "chrome/" + (minimal ? "monochrome_public_minimal_apks/" : "") + "chrome_" + version
diff --git a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java index e220e0a..05d0d8b 100644 --- a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java +++ b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
@@ -42,7 +42,8 @@ public static void setup() throws IOException { assumeTrue(ToolHelper.isLocalDevelopment()); outDirectory = getStaticTemp().newFolder().toPath(); - ZipUtils.unzip(Paths.get("third_party/opensource-apps/tivi/dump_app.zip"), outDirectory); + ZipUtils.unzip( + Paths.get(ToolHelper.THIRD_PARTY_DIR, "opensource-apps/tivi/dump_app.zip"), outDirectory); } @Test
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/BigDecimalMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/BigDecimalMethods.java new file mode 100644 index 0000000..8faac64 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/BigDecimalMethods.java
@@ -0,0 +1,21 @@ +// Copyright (c) 2023, 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.desugar.backports; + +import java.math.BigDecimal; +import java.math.BigInteger; + +public final class BigDecimalMethods { + + // Make BigDecimal#stripTrailingZeros consistent (b/301570464). + public static BigDecimal stripTrailingZeros(BigDecimal biggie) { + // If biggie is ZERO, then platform appears to be inconsistent so return our own value. + if (biggie.signum() == 0) { + return new BigDecimal(BigInteger.ZERO, 0); + } + // If biggie is not zero, then platform appears to consistent so forward to the platform method. + return biggie.stripTrailingZeros(); + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java index 3a7cb1e..d11c2f7 100644 --- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java +++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -38,6 +38,7 @@ AtomicReferenceArrayMethods.class, AtomicReferenceFieldUpdaterMethods.class, AtomicReferenceMethods.class, + BigDecimalMethods.class, BooleanMethods.class, ByteMethods.class, CharSequenceMethods.class,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithDynamicDispatchReturnTypePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithDynamicDispatchReturnTypePropagationTest.java new file mode 100644 index 0000000..69a43e0 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithDynamicDispatchReturnTypePropagationTest.java
@@ -0,0 +1,113 @@ +// Copyright (c) 2023, 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.callsites; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoUnusedInterfaceRemoval; +import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import java.util.List; +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 InvokeInterfaceWithDynamicDispatchReturnTypePropagationTest extends TestBase { + + @Parameter(0) + public boolean nonNullFromSiblingMethod; + + @Parameter(1) + public TestParameters parameters; + + @Parameters(name = "{1}, nonNullFromSiblingMethod: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addKeepRules( + "-assumenosideeffects class " + A.class.getTypeName() + " {", + " static boolean NON_NULL return " + nonNullFromSiblingMethod + ";", + "}") + .enableInliningAnnotations() + .enableNoUnusedInterfaceRemovalAnnotations() + .enableNoVerticalClassMergingAnnotations() + .setMinApi(parameters) + .compile() + .inspect( + inspector -> { + ClassSubject mainClassSubject = inspector.clazz(Main.class); + assertThat(mainClassSubject, isPresent()); + assertThat( + mainClassSubject.uniqueMethodWithOriginalName("dead"), + isAbsentIf(nonNullFromSiblingMethod)); + }) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithEmptyOutput(); + } + + static class Main { + + public static void main(String[] args) { + I i = System.currentTimeMillis() > 0 ? new B() : new C(); + Object o = i.m(); + if (o == null) { + dead(); + } + } + + @NeverInline + static void dead() { + System.out.println("Dead!"); + } + } + + @NoUnusedInterfaceRemoval + @NoVerticalClassMerging + interface I { + + Object m(); + } + + @NoVerticalClassMerging + static class A { + + static boolean NON_NULL; + + public Object m() { + if (NON_NULL) { + return new Object(); + } else { + return System.currentTimeMillis() > 0 ? new Object() : null; + } + } + } + + @NoVerticalClassMerging + static class B extends A implements I {} + + static class C extends B { + + @Override + public Object m() { + return new Object(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualToKeptEmptyMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualToKeptEmptyMethodTest.java new file mode 100644 index 0000000..886238b --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualToKeptEmptyMethodTest.java
@@ -0,0 +1,116 @@ +// Copyright (c) 2023, 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.callsites; + +import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +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 com.android.tools.r8.utils.codeinspector.ClassSubject; +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 InvokeVirtualToKeptEmptyMethodTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void test() throws Exception { + R8TestCompileResult appCompileResult = + testForR8(parameters.getBackend()) + .addProgramClasses(Main.class, I.class, A.class, Runner.class) + .addKeepMainRule(Main.class) + .addKeepClassAndMembersRules(I.class, Runner.class) + .setMinApi(parameters) + .compile() + .inspect( + inspector -> { + ClassSubject iClassSubject = inspector.clazz(I.class); + assertThat(iClassSubject, isPresent()); + + MethodSubject fooMethodSubject = + iClassSubject.uniqueMethodWithOriginalName("foo"); + assertThat(fooMethodSubject, isPresent()); + + ClassSubject runnerClassSubject = inspector.clazz(Runner.class); + assertThat(runnerClassSubject, isPresent()); + + MethodSubject callFooMethodSubject = + runnerClassSubject.uniqueMethodWithOriginalName("callFoo"); + assertThat(callFooMethodSubject, isPresent()); + assertThat(callFooMethodSubject, invokesMethod(fooMethodSubject)); + }); + + appCompileResult.run(parameters.getRuntime(), Main.class).assertSuccessWithEmptyOutput(); + + testForR8(parameters.getBackend()) + .addProgramClasses(TestMain.class, B.class) + .addClasspathClasses(I.class, Runner.class) + .addKeepAllClassesRule() + .setMinApi(parameters) + .compile() + .addRunClasspathFiles(appCompileResult.writeToZip()) + .run(parameters.getRuntime(), TestMain.class) + .assertSuccessWithOutputLines("Hello, world!"); + } + + static class Main { + + public static void main(String[] args) { + Runner.callFoo(new A()); + } + } + + static class TestMain { + + public static void main(String[] args) { + Runner.callFoo(new B()); + } + } + + public static class Runner { + + public static void callFoo(I i) { + i.foo(); + } + } + + // @Keep + public interface I { + + void foo(); + } + + static class A implements I { + + @Override + public void foo() { + // Intentionally empty. + } + } + + static class B implements I { + + @Override + public void foo() { + System.out.println("Hello, world!"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithDynamicDispatchReturnTypePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithDynamicDispatchReturnTypePropagationTest.java new file mode 100644 index 0000000..18131ec --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithDynamicDispatchReturnTypePropagationTest.java
@@ -0,0 +1,84 @@ +// Copyright (c) 2023, 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.callsites; + +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.NeverInline; +import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +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 InvokeVirtualWithDynamicDispatchReturnTypePropagationTest 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) + .enableInliningAnnotations() + .enableNoVerticalClassMergingAnnotations() + .setMinApi(parameters) + .compile() + .inspect( + inspector -> { + ClassSubject mainClassSubject = inspector.clazz(Main.class); + assertThat(mainClassSubject, isPresent()); + assertThat(mainClassSubject.uniqueMethodWithOriginalName("dead"), isAbsent()); + }) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithEmptyOutput(); + } + + static class Main { + + public static void main(String[] args) { + A aOrB = System.currentTimeMillis() > 0 ? new A() : new B(); + Object o = aOrB.m(); + if (o == null) { + dead(); + } + } + + @NeverInline + static void dead() { + System.out.println("Dead!"); + } + } + + @NoVerticalClassMerging + static class A { + + Object m() { + return new Object(); + } + } + + static class B extends A { + + @Override + Object m() { + return new Object(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/MethodResolutionOptimizationInfoWithRuntimeGeneratedLambdaTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/MethodResolutionOptimizationInfoWithRuntimeGeneratedLambdaTest.java new file mode 100644 index 0000000..9f969fd --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/MethodResolutionOptimizationInfoWithRuntimeGeneratedLambdaTest.java
@@ -0,0 +1,66 @@ +// Copyright (c) 2023, 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.callsites; + +import com.android.tools.r8.ReprocessMethod; +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 MethodResolutionOptimizationInfoWithRuntimeGeneratedLambdaTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .enableReprocessMethodAnnotations() + .setMinApi(parameters) + .compile() + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("Hello, world!"); + } + + static class Main { + + @ReprocessMethod + public static void main(String[] args) { + I a = new A(); + I b = () -> ", world!"; + boolean alwaysTrue = System.currentTimeMillis() > 0; + I aInDisguise = alwaysTrue ? a : b; + I bInDisguise = alwaysTrue ? b : a; + System.out.print(aInDisguise.m()); + System.out.println(bInDisguise.m()); + } + } + + interface I { + + String m(); + } + + static class A implements I { + + @Override + public String m() { + return "Hello"; + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeNotNullTest.java index cb0e949..e592d03 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeNotNullTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeNotNullTest.java
@@ -47,10 +47,10 @@ .addKeepMainRule(Main.class) .addKeepRules( "-" + flavor + " class " + Factory.class.getTypeName() + " {", - " java.lang.Object create() return 1;", + " java.lang.Object create() return _NONNULL_;", "}", "-" + flavor + " class " + Singleton.class.getTypeName() + " {", - " java.lang.Object INSTANCE return 1;", + " java.lang.Object INSTANCE return _NONNULL_;", "}") .enableInliningAnnotations() .setMinApi(parameters)
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinTypeReferenceTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinTypeReferenceTest.java new file mode 100644 index 0000000..824d83b --- /dev/null +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinTypeReferenceTest.java
@@ -0,0 +1,43 @@ +// Copyright (c) 2023, 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.kotlin; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.graph.DexItemFactory; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KotlinTypeReferenceTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public KotlinTypeReferenceTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testRef() { + // This is a unit test for b/299878903. + DexItemFactory factory = new DexItemFactory(); + KotlinTypeReference bar = + KotlinTypeReference.fromBinaryNameOrKotlinClassifier( + "com/bar/Foo.Bar", factory, "com.bar.Foo.Bar"); + Assert.assertEquals(bar.toString(), "Lcom/bar/Foo$Bar;"); + KotlinTypeReference kotlinTypeReference = + KotlinTypeReference.fromBinaryNameOrKotlinClassifier( + "com/bar/Foo$Bar", factory, "com.bar.Foo.Bar"); + Assert.assertEquals(kotlinTypeReference.toString(), "Lcom/bar/Foo$Bar;"); + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/bridge/Creator.java b/src/test/java/com/android/tools/r8/naming/bridge/Creator.java index 69094e1..6e7217a 100644 --- a/src/test/java/com/android/tools/r8/naming/bridge/Creator.java +++ b/src/test/java/com/android/tools/r8/naming/bridge/Creator.java
@@ -6,7 +6,7 @@ public class Creator { - public static TesterImpl create() { + public static Tester create() { return new TesterImpl(); } }
diff --git a/src/test/java/com/android/tools/r8/naming/bridge/RenameBridgeMethodJavacTest.java b/src/test/java/com/android/tools/r8/naming/bridge/RenameBridgeMethodJavacTest.java index 94fd3f7..749ef09 100644 --- a/src/test/java/com/android/tools/r8/naming/bridge/RenameBridgeMethodJavacTest.java +++ b/src/test/java/com/android/tools/r8/naming/bridge/RenameBridgeMethodJavacTest.java
@@ -41,11 +41,12 @@ ToolHelper.getSourceFileForTestClass(Tester.class), ToolHelper.getSourceFileForTestClass(TesterImpl.class)) .compile(); - ProcessResult processResult = compileAgainstLibrary(library); + Path output = temp.newFolder().toPath(); + ProcessResult processResult = compileAgainstLibrary(library, output); assertEquals(0, processResult.exitCode); testForJvm(parameters) .addRunClasspathFiles(library) - .addRunClasspathFiles(library.getParent()) + .addRunClasspathFiles(output) .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutputLines("Hello World!"); } @@ -61,18 +62,22 @@ Creator.class, Result.class, ResultImpl.class, Tester.class) .addKeepClassAndMembersRulesWithAllowObfuscation(TesterImpl.class) .compile(); - Path libraryZip = libraryCompileResult.writeToZip(); - ProcessResult processResult = compileAgainstLibrary(libraryZip); - // TODO(b/290711987): The minifier renames the target of a bridge preventing additional - // compilations to succeed. - assertEquals(1, processResult.exitCode); + Path library = libraryCompileResult.writeToZip(); + Path output = temp.newFolder().toPath(); + ProcessResult processResult = compileAgainstLibrary(library, output); + assertEquals(0, processResult.exitCode); + testForJvm(parameters) + .addProgramClasses(Main.class) + .addRunClasspathFiles(library) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("Hello World!"); } - private ProcessResult compileAgainstLibrary(Path classPath) throws Exception { + private ProcessResult compileAgainstLibrary(Path classPath, Path outputPath) throws Exception { return javac(parameters.asCfRuntime()) .addSourceFiles(ToolHelper.getSourceFileForTestClass(Main.class)) .addClasspathFiles(classPath) - .setOutputPath(classPath.getParent()) + .setOutputPath(outputPath) .compileRaw(); } }
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java index 7ddf873..d2cc308 100644 --- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java +++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -156,10 +156,7 @@ 15, FILENAME_INLINE_INSTANCE), LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 7, mainFileName)); - // TODO(b/202811699): Retracing fails to find the right file for the inline frame. - if (false) { - checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack); - } + checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack); }); }
diff --git a/tools/archive.py b/tools/archive.py index ce558e9..7beaede 100755 --- a/tools/archive.py +++ b/tools/archive.py
@@ -85,8 +85,9 @@ def GetVersionDestination(storage_prefix, version_or_path, is_main, new_gradle=False): + assert new_gradle archive_dir = 'raw/main' if is_main else 'raw' - bucket = ARCHIVE_BUCKET if not new_gradle else "r8-test-results" + bucket = ARCHIVE_BUCKET return '%s%s/%s/%s' % (storage_prefix, bucket, archive_dir, version_or_path) @@ -115,12 +116,10 @@ def Main(): (options, args) = ParseOptions() - Run(options, False) - # Clean to ensure that we actually generate all of the artifacts - shutil.rmtree(utils.BUILD) Run(options, True) def Run(options, new_gradle): + assert new_gradle if not utils.is_bot() and not options.dry_run: raise Exception('You are not a bot, don\'t archive builds. ' + 'Use --dry-run to test locally')
diff --git a/tools/compiledump.py b/tools/compiledump.py index 6474441..60f50ac 100755 --- a/tools/compiledump.py +++ b/tools/compiledump.py
@@ -621,7 +621,7 @@ print("=" * 80) print(" RETRACED OUTPUT") print("=" * 80) - retrace.run(get_map_file(version, temp), stacktrace, no_r8lib=False) + retrace.run(get_map_file(version, temp), stacktrace, None, no_r8lib=False) return True except Exception as e2: print("Failed to retrace for version: %s" % version)
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py index 137f584..f93213a 100755 --- a/tools/create_maven_release.py +++ b/tools/create_maven_release.py
@@ -4,19 +4,21 @@ # BSD-style license that can be found in the LICENSE file. import argparse -import gradle import hashlib import jdk -import json -from os import makedirs -from os.path import join, basename -from shutil import copyfile, make_archive, move, rmtree import subprocess import sys -from string import Template -import tempfile -import utils import zipfile +from os import makedirs +from os.path import basename +from os.path import join +from shutil import copyfile +from shutil import make_archive +from shutil import move +from string import Template + +import gradle +import utils LICENSETEMPLATE = Template( """ @@ -313,7 +315,8 @@ 'com.android.tools.r8.ir.desugar.desugaredlibrary.lint.GenerateDesugaredLibraryLintFiles', configuration, implementation, - lint_dir] + lint_dir, + utils.get_android_jar(34)] utils.PrintCmd(cmd) subprocess.check_call(cmd)
diff --git a/tools/golem_build.py b/tools/golem_build.py index 4f668d6..1cbf39f 100755 --- a/tools/golem_build.py +++ b/tools/golem_build.py
@@ -17,11 +17,17 @@ return [ item.lower() for item in items ] def Main(): + # TODO(b/302999071): Move java based benchmarking to new gradle targets = set() - targets.update(lower(run_benchmark.GOLEM_BUILD_TARGETS)) - targets.update(lower(run_on_app_dump.GOLEM_BUILD_TARGETS)) + targets.update(lower(run_benchmark.GOLEM_BUILD_TARGETS_OLD)) cmd = GRADLE_ARGS + [target for target in targets] gradle.RunGradle(cmd) + targets = set() + targets.update(lower(run_benchmark.GOLEM_BUILD_TARGETS_NEW)) + targets.update(lower(run_on_app_dump.GOLEM_BUILD_TARGETS)) + cmd = GRADLE_ARGS + [target for target in targets] + gradle.RunGradle(cmd, new_gradle=True) + if __name__ == '__main__': sys.exit(Main())
diff --git a/tools/run_benchmark.py b/tools/run_benchmark.py index 41da451..c066678 100755 --- a/tools/run_benchmark.py +++ b/tools/run_benchmark.py
@@ -16,11 +16,13 @@ NONLIB_BUILD_TARGET = 'R8WithRelocatedDeps' NONLIB_TEST_BUILD_TARGETS = [utils.R8_TESTS_TARGET, utils.R8_TESTS_DEPS_TARGET] -R8LIB_BUILD_TARGET = utils.R8LIB +# TODO(b/300387869): Cleanup targets +R8LIB_BUILD_TARGET = ':test:r8LibWithRelocatedDeps' R8LIB_TEST_BUILD_TARGETS = [utils.R8LIB_TESTS_TARGET, utils.R8LIB_TESTS_DEPS_TARGET] # The r8lib target is always the golem target. -GOLEM_BUILD_TARGETS = [R8LIB_BUILD_TARGET] + R8LIB_TEST_BUILD_TARGETS +GOLEM_BUILD_TARGETS_OLD = R8LIB_TEST_BUILD_TARGETS +GOLEM_BUILD_TARGETS_NEW = [R8LIB_BUILD_TARGET] def get_golem_resource_path(benchmark): return os.path.join('benchmarks', benchmark) @@ -87,7 +89,7 @@ testjars = [utils.R8_TESTS_DEPS_JAR, utils.R8_TESTS_JAR] else: testBuildTargets = R8LIB_TEST_BUILD_TARGETS - buildTargets = GOLEM_BUILD_TARGETS + buildTargets = GOLEM_BUILD_TARGETS_OLD + GOLEM_BUILD_TARGETS_NEW r8jar = utils.R8LIB_JAR testjars = [utils.R8LIB_TESTS_DEPS_JAR, utils.R8LIB_TESTS_JAR]
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py index 2b07526..8602ea9 100755 --- a/tools/run_on_app_dump.py +++ b/tools/run_on_app_dump.py
@@ -23,7 +23,9 @@ import update_prebuilds_in_android import utils -GOLEM_BUILD_TARGETS = ['R8Lib', 'R8Retrace'] +# TODO(b/300387869): Cleanup targets +GOLEM_BUILD_TARGETS = [':test:r8LibWithRelocatedDeps', + ':test:retraceWithRelocatedDeps'] SHRINKERS = ['r8', 'r8-full', 'r8-nolib', 'r8-nolib-full'] class AttrDict(dict): @@ -689,7 +691,7 @@ 'dump': dump_for_app(app_dir, app), 'r8_jar': get_r8_jar(options, temp_dir, shrinker), 'r8_flags': options.r8_flags, - 'ea': not options.disable_assertions, + 'disable_assertions': options.disable_assertions, 'version': options.version, 'compiler': 'r8full' if is_full_r8(shrinker) else 'r8', 'debug_agent': options.debug_agent, @@ -755,7 +757,7 @@ args = AttrDict({ 'dump': dump_test_for_app(app_dir, app), 'r8_jar': get_r8_jar(options, temp_dir, shrinker), - 'ea': not options.disable_assertions, + 'disable_assertions': options.disable_assertions, 'version': options.version, 'compiler': 'r8full' if is_full_r8(shrinker) else 'r8', 'debug_agent': options.debug_agent, @@ -1136,7 +1138,7 @@ print_indented('%s.fromRevision = 9700;' % options, indentation); print_indented('%s.mainFile = "tools/run_on_app_dump.py "' % options, indentation) - print_indented('"--golem --quiet --shrinker %s --app %s "' + print_indented('"--golem --disable-assertions --quiet --shrinker %s --app %s "' % (shrinker, app.name), indentation + 4) print_indented('"--minify %s --optimize %s --shrink %s";'
diff --git a/tools/test.py b/tools/test.py index 0dbc827..0437701 100755 --- a/tools/test.py +++ b/tools/test.py
@@ -118,6 +118,9 @@ result.add_argument('--command-cache-dir', '--command_cache_dir', help='Cache command invocations to this directory, speeds up test runs', default=os.environ.get('R8_COMMAND_CACHE_DIR')) + result.add_argument('--command-cache-stats', '--command_cache_stats', + help='Collect and print statistics about the command cache.', + default=False, action='store_true') result.add_argument('--java-home', '--java_home', help='Use a custom java version to run tests.') result.add_argument('--java-max-memory-size', '--java_max_memory_size', @@ -204,7 +207,7 @@ default=False, action='store_true') result.add_argument('--new-gradle', help='Specify to run in the new gradle setup', - default=False, action='store_true') + default=True, action='store_true') return result.parse_known_args() def has_failures(classes_file): @@ -243,6 +246,8 @@ if utils.is_bot(): gradle.RunGradle(['--no-daemon', 'clean'], new_gradle=options.new_gradle) print('Running with python ' + str(sys.version_info)) + # Always print stats on bots if command cache is enabled + options.command_cache_stats = options.command_cache_dir is not None desugar_jdk_json_dir = None if options.desugared_library_configuration: @@ -324,6 +329,15 @@ gradle_args.append('-Pcommand_cache_dir=' + options.command_cache_dir) if not os.path.exists(options.command_cache_dir): os.makedirs(options.command_cache_dir) + if options.command_cache_stats: + stats_dir = os.path.join(options.command_cache_dir, 'stats') + gradle_args.append('-Pcommand_cache_stats_dir=' + stats_dir) + if not os.path.exists(stats_dir): + os.makedirs(stats_dir) + # Clean out old stats files + for (_, _, file_names) in os.walk(stats_dir): + for f in file_names: + os.remove(os.path.join(stats_dir, f)) if options.java_home: gradle_args.append('-Dorg.gradle.java.home=' + options.java_home) if options.java_max_memory_size: @@ -490,8 +504,7 @@ utils.upload_file_to_cloud_storage(archive, 'gs://r8-test-results/golden-files/' + archive) - if return_code != 0: - return archive_and_return(return_code, options) + return archive_and_return(return_code, options) return 0 @@ -499,6 +512,23 @@ if return_code != 0: if options.archive_failures: archive_failures(options) + if options.command_cache_stats: + stats_dir = os.path.join(options.command_cache_dir, 'stats') + cache_hit = 0 + cache_miss = 0 + cache_put = 0 + for (_, _, file_names) in os.walk(stats_dir): + for f in file_names: + if f.endswith('CACHEHIT'): + cache_hit += os.stat(os.path.join(stats_dir, f)).st_size + if f.endswith('CACHEMISS'): + cache_miss += os.stat(os.path.join(stats_dir, f)).st_size + if f.endswith('CACHEPUT'): + cache_put += os.stat(os.path.join(stats_dir, f)).st_size + print('Command cache stats') + print(' Cache hits: ' + str(cache_hit)) + print(' Cache miss: ' + str(cache_miss)) + print(' Cache puts: ' + str(cache_put)) return return_code def print_jstacks():