Merge commit 'c0b0127d98ec62b55e00c0bdba94652ceb5bca08' into dev-release
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index 5071174..ae4700f 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.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.DefaultInstanceInitializerCode;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -98,13 +99,6 @@
}
private void checkReferenceToLibraryClass(DexReference reference) {
- if (reference.isDexMember()) {
- reference
- .asDexMember()
- .getReferencedBaseTypes(appView.dexItemFactory())
- .forEach(
- refType -> findReferencedLibraryClasses(appView.graphLens().lookupType(refType)));
- }
DexType rewrittenType = appView.graphLens().lookupType(reference.getContextType());
findReferencedLibraryClasses(rewrittenType);
if (reference.isDexMethod()) {
@@ -155,6 +149,11 @@
}
public void processClass(DexProgramClass clazz) {
+ if (appView
+ .getSyntheticItems()
+ .isSyntheticOfKind(clazz.getType(), SyntheticKind.API_MODEL_OUTLINE)) {
+ return;
+ }
findReferencedLibraryClasses(clazz.type);
clazz.forEachProgramMethodMatching(
DexEncodedMethod::hasCode,
@@ -285,14 +284,20 @@
|| !method.isStatic()
|| appView.options().canUseDefaultAndStaticInterfaceMethods();
DexMethod newMethod = method.getReference().withHolder(clazz.type, appView.dexItemFactory());
- boolean isVirtualInterfaceMethod = method.isVirtualMethod() && clazz.isInterface();
- return DexEncodedMethod.syntheticBuilder(method)
- .setMethod(newMethod)
- .modifyAccessFlags(MethodAccessFlags::setSynthetic)
- .applyIf(isVirtualInterfaceMethod, b -> b.modifyAccessFlags(MethodAccessFlags::setAbstract))
- .applyIf(
- !isVirtualInterfaceMethod && !method.isAbstract(),
- b -> b.modifyAccessFlags(MethodAccessFlags::setNative))
- .build();
+ DexEncodedMethod.Builder methodBuilder =
+ DexEncodedMethod.syntheticBuilder(method)
+ .setMethod(newMethod)
+ .modifyAccessFlags(MethodAccessFlags::setSynthetic);
+ if (method.isInstanceInitializer()) {
+ methodBuilder.setCode(DefaultInstanceInitializerCode.get());
+ } else if (method.isVirtualMethod() && clazz.isInterface()) {
+ methodBuilder.modifyAccessFlags(MethodAccessFlags::setAbstract);
+ } else if (method.isAbstract()) {
+ methodBuilder.modifyAccessFlags(MethodAccessFlags::setAbstract);
+ } else {
+ // To allow us not adding a trivial throwing code body we set the access flag as native.
+ methodBuilder.modifyAccessFlags(MethodAccessFlags::setNative);
+ }
+ return methodBuilder.build();
}
}
diff --git a/src/main/java/com/android/tools/r8/contexts/CompilationContext.java b/src/main/java/com/android/tools/r8/contexts/CompilationContext.java
index d3db39a..a34811c 100644
--- a/src/main/java/com/android/tools/r8/contexts/CompilationContext.java
+++ b/src/main/java/com/android/tools/r8/contexts/CompilationContext.java
@@ -34,11 +34,13 @@
}
private final Consumer<String> testingConsumer;
+ private final Thread mainThread;
private final Map<String, String> seenSetForTesting = new ConcurrentHashMap<>();
private int nextProcessorId = 0;
private CompilationContext(InternalOptions options) {
testingConsumer = options.testing.processingContextsConsumer;
+ mainThread = options.mainThread;
}
private boolean verifyContext(ContextDescriptorProvider context) {
@@ -63,6 +65,7 @@
public ProcessorContext createProcessorContext() {
ProcessorContext processorContext = new ProcessorContext(this, nextProcessorId++);
assert verifyContext(processorContext);
+ assert mainThread == Thread.currentThread() : "Invoked on another thread than main";
return processorContext;
}
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 850b669..b9d02f9 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
@@ -211,7 +211,17 @@
if (other.isNull()) {
return NullOrAbstractValue.create(this);
}
+ if (isNullOrAbstractValue() && asNullOrAbstractValue().getNonNullValue().equals(other)) {
+ return this;
+ }
+ if (other.isNullOrAbstractValue()
+ && other.asNullOrAbstractValue().getNonNullValue().equals(this)) {
+ return other;
+ }
+ return unknown();
}
+ assert !isNullOrAbstractValue();
+ assert !other.isNullOrAbstractValue();
if (allowNonConstantNumbers
&& isConstantOrNonConstantNumberValue()
&& other.isConstantOrNonConstantNumberValue()) {
@@ -230,7 +240,7 @@
}
return numberFromSetValueBuilder.build(factory);
}
- return UnknownValue.getInstance();
+ return unknown();
}
public abstract AbstractValue rewrittenWithLens(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
index d56be1a..da44c03 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
@@ -92,13 +92,18 @@
if (clazz == null || !clazz.isLibraryClass()) {
return appView.computedMinApiLevel();
}
- ComputedApiLevel apiLevel =
+ ComputedApiLevel methodApiLevel =
apiLevelCompute.computeApiLevelForLibraryReference(
cfInvoke.getMethod(), ComputedApiLevel.unknown());
- if (apiLevel.isGreaterThan(appView.computedMinApiLevel())) {
- return apiLevel;
+ if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(methodApiLevel)) {
+ return appView.computedMinApiLevel();
}
- return appView.computedMinApiLevel();
+ // Compute the api level of the holder to see if the method will be stubbed.
+ ComputedApiLevel holderApiLevel =
+ apiLevelCompute.computeApiLevelForLibraryReference(holderType, ComputedApiLevel.unknown());
+ return methodApiLevel.isGreaterThan(holderApiLevel)
+ ? methodApiLevel
+ : appView.computedMinApiLevel();
}
private Collection<CfInstruction> desugarLibraryCall(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
index 9e79ee0..5022d48 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.kotlin;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.isValidMethodDescriptor;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toDefaultJvmMethodSignature;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
import com.android.tools.r8.graph.AppView;
@@ -18,6 +19,8 @@
import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.ImmutableList;
+import com.google.common.math.IntMath;
+import java.math.RoundingMode;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -87,7 +90,7 @@
}
continue;
}
- keepIfInline(kmFunction.getFlags(), method, keepByteCode);
+ keepIfInline(kmFunction.getFlags(), method, signature, methodSignatureMap, keepByteCode);
method.setKotlinMemberInfo(kotlinFunctionInfo);
originalAssignmentTracker.add(method.getReference());
}
@@ -138,8 +141,24 @@
}
private static void keepIfInline(
- int flags, DexEncodedMethod method, Consumer<DexEncodedMethod> keepByteCode) {
+ int flags,
+ DexEncodedMethod method,
+ JvmMethodSignature signature,
+ Map<String, DexEncodedMethod> methodSignatureMap,
+ Consumer<DexEncodedMethod> keepByteCode) {
if (Flags.IS_INLINE.get(flags)) {
+ // Check if we can find a default method. If there are more than 32 arguments another int
+ // index will be added to the default method.
+ for (int i = 1;
+ i <= IntMath.divide(method.getParameters().size(), 32, RoundingMode.CEILING);
+ i++) {
+ DexEncodedMethod defaultValueMethod =
+ methodSignatureMap.get(toDefaultJvmMethodSignature(signature, i).toString());
+ if (defaultValueMethod != null) {
+ keepByteCode.accept(defaultValueMethod);
+ return;
+ }
+ }
keepByteCode.accept(method);
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
index 2d6f39a..1d927fe 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.shaking.ProguardKeepRuleType;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Pair;
+import com.google.common.base.Strings;
import kotlinx.metadata.KmExtensionType;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmPropertyExtensionVisitor;
@@ -99,13 +100,21 @@
return new JvmMethodSignature(method.name.toString(), descBuilder.toString());
}
+ static JvmMethodSignature toDefaultJvmMethodSignature(
+ JvmMethodSignature methodSignature, int intArguments) {
+ return new JvmMethodSignature(
+ methodSignature.getName() + "$default",
+ methodSignature
+ .getDesc()
+ .replace(")", Strings.repeat("I", intArguments) + "Ljava/lang/Object;)"));
+ }
+
static class KmPropertyProcessor {
private JvmFieldSignature fieldSignature = null;
// Custom getter via @get:JvmName("..."). Otherwise, null.
private JvmMethodSignature getterSignature = null;
// Custom getter via @set:JvmName("..."). Otherwise, null.
private JvmMethodSignature setterSignature = null;
-
KmPropertyProcessor(KmProperty kmProperty) {
kmProperty.accept(
new KmPropertyVisitor() {
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
index 6961fb0..ef9ae30 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
@@ -179,6 +179,15 @@
boolean containsTypeOfKind(DexType type, SyntheticKind kind) {
List<SyntheticProgramClassReference> synthetics = nonLegacyClasses.get(type);
if (synthetics == null) {
+ List<SyntheticMethodReference> syntheticMethodReferences = nonLegacyMethods.get(type);
+ if (syntheticMethodReferences == null) {
+ return false;
+ }
+ for (SyntheticMethodReference syntheticMethodReference : syntheticMethodReferences) {
+ if (syntheticMethodReference.getKind() == kind) {
+ return true;
+ }
+ }
return false;
}
for (SyntheticProgramClassReference synthetic : synthetics) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 7bc38a4..7ce8109 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -315,6 +315,9 @@
return 16383;
}
+ // We assume options will always be created on the main thread.
+ public Thread mainThread = Thread.currentThread();
+
public boolean enableSwitchRewriting = true;
public boolean enableStringSwitchConversion = true;
public int minimumStringSwitchSize = 3;
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java b/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java
index e97d8c5..bff70f5 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java
@@ -163,8 +163,9 @@
operand, block, originalType, rewrittenType, isCodeFullyRewrittenWithLens);
assert !needsCastForOperand.isUnknown();
if (needsCastForOperand.isTrue()) {
- insertCastForOperand(
- operand, rewrittenType, instruction, blockIterator, block, instructionIterator);
+ instructionIterator =
+ insertCastForOperand(
+ operand, rewrittenType, instruction, blockIterator, block, instructionIterator);
}
}
}
@@ -181,7 +182,7 @@
.addLast(new WorklistItem(operandIndex, originalType, rewrittenType));
}
- private void insertCastForOperand(
+ private InstructionListIterator insertCastForOperand(
Value operand,
DexType castType,
Instruction rewrittenUser,
@@ -199,10 +200,11 @@
.setPosition(rewrittenUser)
.build();
if (block.hasCatchHandlers()) {
- instructionIterator
- .splitCopyCatchHandlers(code, blockIterator, appView.options())
- .listIterator(code)
- .add(checkCast);
+ BasicBlock splitBlock =
+ instructionIterator.splitCopyCatchHandlers(code, blockIterator, appView.options());
+ instructionIterator.previous();
+ instructionIterator.add(checkCast);
+ instructionIterator = splitBlock.listIterator(code);
} else {
instructionIterator.add(checkCast);
}
@@ -210,6 +212,7 @@
Instruction next = instructionIterator.next();
assert next == rewrittenUser;
+ return instructionIterator;
}
private boolean isOperandRewrittenWithLens(
diff --git a/src/test/java/com/android/tools/r8/D8TestCompileResult.java b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
index 3908d64..6d7634c 100644
--- a/src/test/java/com/android/tools/r8/D8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
@@ -5,10 +5,7 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.AndroidApp;
-import java.io.IOException;
-import java.nio.file.Path;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
public class D8TestCompileResult extends TestCompileResult<D8TestCompileResult, D8TestRunResult> {
private final String proguardMap;
@@ -57,13 +54,4 @@
public D8TestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
return new D8TestRunResult(app, runtime, result, proguardMap);
}
-
- public D8TestRunResult runWithJaCoCo(
- Path output, TestRuntime runtime, String mainClass, String... args)
- throws IOException, ExecutionException {
- setSystemProperty("jacoco-agent.destfile", output.toString());
- setSystemProperty("jacoco-agent.dumponexit", "true");
- setSystemProperty("jacoco-agent.output", "file");
- return run(runtime, mainClass, args);
- }
}
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index b6bcf5c..180b043 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -180,6 +180,8 @@
private final List<Path> classpath = new ArrayList<>();
private final List<String> additionalArguments = new ArrayList<>();
private boolean useJvmAssertions;
+ // TODO(b/211590675): We should enable assertions by default.
+ private boolean enableAssertions = true;
private Path output = null;
private KotlinCompilerTool(
@@ -250,6 +252,11 @@
return this;
}
+ public KotlinCompilerTool disableAssertions() {
+ this.enableAssertions = false;
+ return this;
+ }
+
public KotlinCompilerTool addSourceFilesWithNonKtExtension(
TemporaryFolder temp, Collection<Path> files) {
return addSourceFiles(
@@ -325,6 +332,9 @@
private ProcessResult compileInternal(Path output) throws IOException {
List<String> cmdline = new ArrayList<>();
cmdline.add(jdk.getJavaExecutable().toString());
+ if (enableAssertions) {
+ cmdline.add("-ea");
+ }
cmdline.add("-cp");
cmdline.add(compiler.getCompiler().toString());
cmdline.add(ToolHelper.K2JVMCompiler);
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index ebf30b6..5b6996d 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -217,6 +217,14 @@
ObjectArrays.concat(mainClassSubject.getFinalName(), args));
}
+ public RR runWithJaCoCo(Path output, TestRuntime runtime, String mainClass, String... args)
+ throws IOException, ExecutionException {
+ setSystemProperty("jacoco-agent.destfile", output.toString());
+ setSystemProperty("jacoco-agent.dumponexit", "true");
+ setSystemProperty("jacoco-agent.output", "file");
+ return run(runtime, mainClass, args);
+ }
+
public CR addRunClasspathFiles(Path... classpath) {
return addRunClasspathFiles(Arrays.asList(classpath));
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelDesugaredLibraryReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelDesugaredLibraryReferenceTest.java
index c3c1940..0d451cb 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelDesugaredLibraryReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelDesugaredLibraryReferenceTest.java
@@ -65,7 +65,10 @@
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Executor.class)
.assertSuccessWithOutputLines("Z")
- .inspect(ApiModelingTestHelper.verifyThat(parameters, printZone).inlinedInto(main));
+ .inspect(
+ inspector ->
+ ApiModelingTestHelper.verifyThat(inspector, parameters, printZone)
+ .inlinedInto(main));
}
static class Executor {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassDifferentApiLevelTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassDifferentApiLevelTest.java
index 8002d8c..911731b 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassDifferentApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassDifferentApiLevelTest.java
@@ -47,7 +47,7 @@
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.enableInliningAnnotations()
.compile()
- .inspect(verifyThat(parameters, callApi).inlinedInto(callCallApi))
+ .inspect(inspector -> verifyThat(inspector, parameters, callApi).inlinedInto(callCallApi))
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Api::apiLevel22");
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java
index 1ccb626..8d4e2f1 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java
@@ -53,10 +53,10 @@
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
- .inspect(verifyThat(parameters, notCallingApi).inlinedIntoFromApiLevel(main, L_MR1))
.inspect(
inspector -> {
// No matter the api level, we should always inline callingApi into notCallingApi.
+ verifyThat(inspector, parameters, notCallingApi).inlinedIntoFromApiLevel(main, L_MR1);
assertThat(inspector.method(callingApi), not(isPresent()));
if (parameters.isDexRuntime()
&& parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
index 03b2f3e..9d667c1 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
@@ -52,8 +52,12 @@
.addRunClasspathClasses(ApiType.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(ApiType.class.getName())
- .inspect(verifyThat(parameters, apiCallerApiLevel22).inlinedInto(apiCallerCallerApiLevel22))
- .inspect(inspector -> assertThat(inspector.clazz(OtherCaller.class), not(isPresent())));
+ .inspect(
+ inspector -> {
+ verifyThat(inspector, parameters, apiCallerApiLevel22)
+ .inlinedInto(apiCallerCallerApiLevel22);
+ assertThat(inspector.clazz(OtherCaller.class), not(isPresent()));
+ });
}
public static class ApiType {}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
new file mode 100644
index 0000000..7c60172
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+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 ApiModelMockClassInstanceInitTest extends TestBase {
+
+ private final AndroidApiLevel mockLevel = AndroidApiLevel.M;
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/197078995): Make this work on 12.
+ assumeFalse(
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+ boolean isMockApiLevel =
+ parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, TestClass.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addAndroidBuildVersion()
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
+ .enableInliningAnnotations()
+ .compile()
+ .applyIf(isMockApiLevel, b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(isMockApiLevel, "LibraryClass::foo")
+ .assertSuccessWithOutputLinesIf(!isMockApiLevel, "NoClassDefFoundError")
+ .inspect(
+ inspector ->
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel))
+ .applyIf(
+ !isMockApiLevel
+ && parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0),
+ result -> result.assertStderrMatches(not(containsString("This dex file is invalid"))));
+ }
+
+ // Only present from api level 23.
+ public static class LibraryClass {
+
+ public void foo() {
+ System.out.println("LibraryClass::foo");
+ }
+ }
+
+ public static class TestClass {
+
+ @NeverInline
+ public static void test() {
+ try {
+ new LibraryClass().foo();
+ } catch (ExceptionInInitializerError | NoClassDefFoundError er) {
+ System.out.println("NoClassDefFoundError");
+ }
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ TestClass.test();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
index 3c5ad21..dddfcbc 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -60,7 +60,9 @@
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLinesIf(isMockApiLevel, "LibraryClass::foo")
.assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")
- .inspect(verifyThat(parameters, LibraryClass.class).stubbedUntil(mockLevel));
+ .inspect(
+ inspector ->
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel));
}
// Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
index 2268036..474874c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -59,7 +59,9 @@
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLinesIf(isMockApiLevel, "ProgramClass::foo")
.assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")
- .inspect(verifyThat(parameters, LibraryClass.class).stubbedUntil(mockLevel));
+ .inspect(
+ inspector ->
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel));
}
// Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
index a5f30c2..6c8b93d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -73,9 +73,13 @@
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLinesIf(isMockApiLevel, "ProgramClass::foo")
.assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")
- .inspect(verifyThat(parameters, LibraryClass.class).stubbedUntil(lowerMockApiLevel))
- .inspect(verifyThat(parameters, LibraryInterface.class).stubbedUntil(lowerMockApiLevel))
- .inspect(verifyThat(parameters, OtherLibraryClass.class).stubbedUntil(mockApiLevel));
+ .inspect(
+ inspector -> {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(lowerMockApiLevel);
+ verifyThat(inspector, parameters, LibraryInterface.class)
+ .stubbedUntil(lowerMockApiLevel);
+ verifyThat(inspector, parameters, OtherLibraryClass.class).stubbedUntil(mockApiLevel);
+ });
}
// Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
index e752764..45d2bec 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.apimodel;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestParameters;
@@ -68,8 +69,10 @@
parameters.getDexRuntimeVersion().isNewerThan(Version.V7_0_0),
result -> result.assertSuccessWithOutputLines("~"))
.inspect(
- ApiModelingTestHelper.verifyThat(parameters, printZone)
- .inlinedIntoFromApiLevel(main, AndroidApiLevel.O));
+ inspector -> {
+ verifyThat(inspector, parameters, printZone)
+ .inlinedIntoFromApiLevel(main, AndroidApiLevel.O);
+ });
}
static class Executor {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
index 9446479..ecbd92e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
@@ -54,7 +54,10 @@
.apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
- .inspect(verifyThat(parameters, apiCaller).inlinedIntoFromApiLevel(apiCallerCaller, L_MR1))
+ .inspect(
+ inspector ->
+ verifyThat(inspector, parameters, apiCaller)
+ .inlinedIntoFromApiLevel(apiCallerCaller, L_MR1))
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Hello World!");
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
index a480188..d39b038 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
@@ -53,8 +53,9 @@
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::callInterfaceMethod")
.inspect(
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1));
+ inspector ->
+ verifyThat(inspector, parameters, apiCaller)
+ .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1));
}
public interface Api {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
index e79cfed..98aa240 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
@@ -48,7 +48,8 @@
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A::apiLevel21", "B::apiLevel22")
- .inspect(verifyThat(parameters, apiLevel22).inlinedInto(apiLevel21));
+ .inspect(
+ inspector -> verifyThat(inspector, parameters, apiLevel22).inlinedInto(apiLevel21));
}
// This tests that program classes where we directly mock the methods to have an api level will
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticFieldTest.java
index 28a2b00..f4465a4 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticFieldTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticFieldTest.java
@@ -51,8 +51,9 @@
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
.inspect(
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
+ inspector ->
+ verifyThat(inspector, parameters, apiCaller)
+ .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Hello World!");
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java
index b5c7134..16f24fa 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java
@@ -50,8 +50,9 @@
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
.inspect(
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
+ inspector ->
+ verifyThat(inspector, parameters, apiCaller)
+ .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
index e1cf2e2..4979620 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
@@ -68,7 +68,9 @@
}))
.compile()
// We do not inline overrides calling super.
- .inspect(verifyThat(parameters, apiCaller).notInlinedInto(apiCallerCaller))
+ .inspect(
+ inspector ->
+ verifyThat(inspector, parameters, apiCaller).notInlinedInto(apiCallerCaller))
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::apiLevel22", "Api::apiLevel22");
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
index a366d8a..2311f12 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
@@ -55,8 +55,9 @@
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
.inspect(
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
+ inspector ->
+ verifyThat(inspector, parameters, apiCaller)
+ .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
index 086cfa4..498ef4c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
@@ -64,16 +64,17 @@
ClassSubject companion = inspector.companionClassFor(ApiCaller.class);
assertThat(companion, isPresent());
FoundClassSubject foundCompanion = companion.asFoundClassSubject();
- verifyThat(parameters, callApiLevel22)
+ verifyThat(inspector, parameters, callApiLevel22)
.setHolder(foundCompanion)
.inlinedIntoFromApiLevel(noApiCallTo22, L_MR1);
- verifyThat(parameters, callApiLevel26)
+ verifyThat(inspector, parameters, callApiLevel26)
.setHolder(foundCompanion)
.inlinedIntoFromApiLevel(noApiCallTo26, O);
} else {
- verifyThat(parameters, callApiLevel22)
+ verifyThat(inspector, parameters, callApiLevel22)
.inlinedIntoFromApiLevel(noApiCallTo22, L_MR1);
- verifyThat(parameters, callApiLevel26).inlinedIntoFromApiLevel(noApiCallTo26, O);
+ verifyThat(inspector, parameters, callApiLevel26)
+ .inlinedIntoFromApiLevel(noApiCallTo26, O);
}
})
.addRunClasspathClasses(Api.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoMockForOutlineTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoMockForOutlineTest.java
new file mode 100644
index 0000000..a4cbd4b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoMockForOutlineTest.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.lang.reflect.Method;
+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 ApiModelNoMockForOutlineTest extends TestBase {
+
+ private final AndroidApiLevel classApiLevel = AndroidApiLevel.K;
+ private final AndroidApiLevel methodApiLevel = AndroidApiLevel.M;
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeFalse(
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+ boolean isMethodApiLevel =
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(methodApiLevel);
+ Method methodOn23 = LibraryClass.class.getDeclaredMethod("methodOn23");
+ Method mainMethod = Main.class.getDeclaredMethod("main", String[].class);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
+ .apply(setMockApiLevelForMethod(methodOn23, methodApiLevel))
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .enableInliningAnnotations()
+ .compile()
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters
+ .getRuntime()
+ .maxSupportedApiLevel()
+ .isGreaterThanOrEqualTo(classApiLevel),
+ b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(!isMethodApiLevel, "Hello World")
+ .assertSuccessWithOutputLinesIf(isMethodApiLevel, "LibraryClass::methodOn23", "Hello World")
+ .inspect(
+ inspector -> {
+ assertThat(inspector.method(mainMethod), isPresent());
+ verifyThat(inspector, parameters, methodOn23)
+ .isOutlinedFromUntil(mainMethod, methodApiLevel);
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(classApiLevel);
+ if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(classApiLevel)) {
+ // We never trace outlined method for stubs so this holds by default.
+ ClassSubject mockedLibraryClass = inspector.clazz(LibraryClass.class);
+ assertThat(mockedLibraryClass, isPresent());
+ MethodSubject mockedMethodOn23 =
+ mockedLibraryClass.uniqueMethodWithName("methodOn23");
+ assertThat(mockedMethodOn23, isAbsent());
+ }
+ });
+ }
+
+ // Only present from api level 19.
+ public static class LibraryClass {
+
+ public void methodOn23() {
+ System.out.println("LibraryClass::methodOn23");
+ }
+ }
+
+ public static class Main {
+
+ @NeverInline
+ public static Object create() {
+ return AndroidBuildVersion.VERSION >= 23 ? new LibraryClass() : null;
+ }
+
+ public static void main(String[] args) {
+ Object libraryClass = create();
+ if (libraryClass != null) {
+ ((LibraryClass) libraryClass).methodOn23();
+ }
+ System.out.println("Hello World");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
new file mode 100644
index 0000000..91c55e4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Method;
+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 ApiModelNoOutlineForFullyMockedTest extends TestBase {
+
+ private final AndroidApiLevel libraryApiLevel = AndroidApiLevel.M;
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeFalse(
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+ boolean isLibraryApiLevel =
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryApiLevel);
+ Method methodOn23 = LibraryClass.class.getDeclaredMethod("methodOn23");
+ Method mainMethod = Main.class.getDeclaredMethod("main", String[].class);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, libraryApiLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, libraryApiLevel))
+ .apply(setMockApiLevelForMethod(methodOn23, libraryApiLevel))
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .enableInliningAnnotations()
+ .compile()
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters
+ .getRuntime()
+ .maxSupportedApiLevel()
+ .isGreaterThanOrEqualTo(libraryApiLevel),
+ b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(!isLibraryApiLevel, "Hello World")
+ .assertSuccessWithOutputLinesIf(
+ isLibraryApiLevel, "LibraryClass::methodOn23", "Hello World")
+ .inspect(
+ inspector -> {
+ assertThat(inspector.method(mainMethod), isPresent());
+ // TODO(b/211720912): We should never outline since the library class and method is
+ // introduced at the same level.
+ verifyThat(inspector, parameters, methodOn23).isNotOutlinedFrom(mainMethod);
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(libraryApiLevel);
+ });
+ }
+
+ // Only present from api level 23.
+ public static class LibraryClass {
+
+ public void methodOn23() {
+ System.out.println("LibraryClass::methodOn23");
+ }
+ }
+
+ public static class Main {
+
+ @NeverInline
+ public static Object create() {
+ return AndroidBuildVersion.VERSION >= 23 ? new LibraryClass() : null;
+ }
+
+ public static void main(String[] args) {
+ Object libraryClass = create();
+ if (libraryClass != null) {
+ ((LibraryClass) libraryClass).methodOn23();
+ }
+ System.out.println("Hello World");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
index e0afd34..0760cad 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
@@ -86,7 +86,8 @@
: 3;
assertEquals(classCount, inspector.allClasses().size());
Method testMethod = TestClass.class.getDeclaredMethod("test");
- verifyThat(parameters, adeddOn23).isOutlinedFromUntil(testMethod, methodApiLevel);
+ verifyThat(inspector, parameters, adeddOn23)
+ .isOutlinedFromUntil(testMethod, methodApiLevel);
if (parameters.isDexRuntime()
&& parameters.getApiLevel().isLessThan(methodApiLevel)) {
// Verify that we invoke the synthesized outline addedOn23 twice.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
index 051f793..a3e2d46 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
@@ -69,11 +69,13 @@
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLinesIf(libraryClassNotStubbed, "LibraryClass::foo")
.assertSuccessWithOutputLinesIf(!libraryClassNotStubbed, "Hello World")
- .inspect(verifyThat(parameters, LibraryClass.class).stubbedUntil(libraryClassLevel))
.inspect(
- verifyThat(parameters, apiMethod)
- .isOutlinedFromUntil(
- Main.class.getDeclaredMethod("main", String[].class), libraryMethodLevel));
+ inspector -> {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(libraryClassLevel);
+ verifyThat(inspector, parameters, apiMethod)
+ .isOutlinedFromUntil(
+ Main.class.getDeclaredMethod("main", String[].class), libraryMethodLevel);
+ });
}
// Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
index 9d0f7cf..7755822 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -113,10 +113,13 @@
.matches(methodSubject))
.findFirst();
assertFalse(synthesizedMissingNotReferenced.isPresent());
- verifyThat(parameters, addedOn23).isOutlinedFromUntil(testMethod, AndroidApiLevel.M);
- verifyThat(parameters, addedOn27)
+ verifyThat(inspector, parameters, addedOn23).isNotOutlinedFrom(testMethod);
+ verifyThat(inspector, parameters, addedOn27)
.isOutlinedFromUntil(testMethod, AndroidApiLevel.O_MR1);
- verifyThat(parameters, LibraryClass.class.getDeclaredMethod("missingAndReferenced"))
+ verifyThat(
+ inspector,
+ parameters,
+ LibraryClass.class.getDeclaredMethod("missingAndReferenced"))
.isOutlinedFrom(testMethod);
if (parameters.getApiLevel().isLessThan(AndroidApiLevel.O_MR1)) {
assertEquals(5, inspector.allClasses().size());
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
index ef4737d..25f0c3a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
@@ -52,8 +52,9 @@
.addAndroidBuildVersion()
.compile()
.inspect(
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.M))
+ inspector ->
+ verifyThat(inspector, parameters, apiCaller)
+ .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.M))
.addRunClasspathClasses(LibraryClass.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLinesIf(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index 70d4f36..7077a21 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -150,49 +149,56 @@
};
}
- static ApiModelingClassVerificationHelper verifyThat(TestParameters parameters, Class<?> clazz) {
- return new ApiModelingClassVerificationHelper(parameters, clazz);
+ static ApiModelingClassVerificationHelper verifyThat(
+ CodeInspector inspector, TestParameters parameters, Class<?> clazz) {
+ return new ApiModelingClassVerificationHelper(inspector, parameters, clazz);
}
- static ApiModelingMethodVerificationHelper verifyThat(TestParameters parameters, Method method) {
- return new ApiModelingMethodVerificationHelper(parameters, Reference.methodFromMethod(method));
+ static ApiModelingMethodVerificationHelper verifyThat(
+ CodeInspector inspector, TestParameters parameters, Method method) {
+ return new ApiModelingMethodVerificationHelper(
+ inspector, parameters, Reference.methodFromMethod(method));
}
public static class ApiModelingClassVerificationHelper {
+ private final CodeInspector inspector;
private final Class<?> classOfInterest;
private final TestParameters parameters;
- public ApiModelingClassVerificationHelper(TestParameters parameters, Class<?> classOfInterest) {
+ public ApiModelingClassVerificationHelper(
+ CodeInspector inspector, TestParameters parameters, Class<?> classOfInterest) {
+ this.inspector = inspector;
this.parameters = parameters;
this.classOfInterest = classOfInterest;
}
- public ThrowingConsumer<CodeInspector, Exception> stubbedUntil(AndroidApiLevel finalApiLevel) {
- return inspector -> {
- assertThat(
- inspector.clazz(classOfInterest),
- notIf(
- isPresent(),
- parameters.isCfRuntime()
- || parameters.getApiLevel().isGreaterThanOrEqualTo(finalApiLevel)));
- };
+ public void stubbedUntil(AndroidApiLevel finalApiLevel) {
+ assertThat(
+ inspector.clazz(classOfInterest),
+ notIf(
+ isPresent(),
+ parameters.isCfRuntime()
+ || parameters.getApiLevel().isGreaterThanOrEqualTo(finalApiLevel)));
}
}
public static class ApiModelingMethodVerificationHelper {
+ private final CodeInspector inspector;
private final MethodReference methodOfInterest;
private final TestParameters parameters;
private ApiModelingMethodVerificationHelper(
- TestParameters parameters, MethodReference methodOfInterest) {
+ CodeInspector inspector, TestParameters parameters, MethodReference methodOfInterest) {
+ this.inspector = inspector;
this.methodOfInterest = methodOfInterest;
this.parameters = parameters;
}
public ApiModelingMethodVerificationHelper setHolder(FoundClassSubject classSubject) {
return new ApiModelingMethodVerificationHelper(
+ inspector,
parameters,
Reference.method(
classSubject.getFinalReference(),
@@ -201,68 +207,62 @@
methodOfInterest.getReturnType()));
}
- ThrowingConsumer<CodeInspector, Exception> inlinedIntoFromApiLevel(
- Method method, AndroidApiLevel apiLevel) {
- return parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevel)
- ? inlinedInto(method)
- : notInlinedInto(method);
+ void inlinedIntoFromApiLevel(Method method, AndroidApiLevel apiLevel) {
+ if (parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevel)) {
+ inlinedInto(method);
+ } else {
+ notInlinedInto(method);
+ }
}
- ThrowingConsumer<CodeInspector, Exception> notInlinedInto(Method method) {
- return inspector -> {
- MethodSubject candidate = inspector.method(methodOfInterest);
- assertThat(candidate, isPresent());
- MethodSubject target = inspector.method(method);
- assertThat(target, isPresent());
- assertThat(target, CodeMatchers.invokesMethod(candidate));
- };
+ void notInlinedInto(Method method) {
+ MethodSubject candidate = inspector.method(methodOfInterest);
+ assertThat(candidate, isPresent());
+ MethodSubject target = inspector.method(method);
+ assertThat(target, isPresent());
+ assertThat(target, CodeMatchers.invokesMethod(candidate));
}
- ThrowingConsumer<CodeInspector, Exception> inlinedInto(Method method) {
- return inspector -> {
- MethodSubject candidate = inspector.method(methodOfInterest);
- if (!candidate.isPresent()) {
- return;
- }
- MethodSubject target = inspector.method(method);
- assertThat(target, isPresent());
- assertThat(target, not(CodeMatchers.invokesMethod(candidate)));
- };
+ void inlinedInto(Method method) {
+ MethodSubject candidate = inspector.method(methodOfInterest);
+ if (!candidate.isPresent()) {
+ return;
+ }
+ MethodSubject target = inspector.method(method);
+ assertThat(target, isPresent());
+ assertThat(target, not(CodeMatchers.invokesMethod(candidate)));
}
- ThrowingConsumer<CodeInspector, Exception> isOutlinedFromUntil(
- Method method, AndroidApiLevel apiLevel) {
- return parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(apiLevel)
- ? isOutlinedFrom(method)
- : isNotOutlinedFrom(method);
+ void isOutlinedFromUntil(Method method, AndroidApiLevel apiLevel) {
+ if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(apiLevel)) {
+ isOutlinedFrom(method);
+ } else {
+ isNotOutlinedFrom(method);
+ }
}
- ThrowingConsumer<CodeInspector, Exception> isOutlinedFrom(Method method) {
- return inspector -> {
- // Check that the call is in a synthetic class.
- List<FoundMethodSubject> outlinedMethod =
- inspector.allClasses().stream()
- .flatMap(clazz -> clazz.allMethods().stream())
- .filter(
- methodSubject ->
- methodSubject.isSynthetic()
- && invokesMethodWithName(methodOfInterest.getMethodName())
- .matches(methodSubject))
- .collect(Collectors.toList());
- assertEquals(1, outlinedMethod.size());
- // Assert that method invokes the outline
- MethodSubject caller = inspector.method(method);
- assertThat(caller, isPresent());
- assertThat(caller, invokesMethod(outlinedMethod.get(0)));
- };
+ void isOutlinedFrom(Method method) {
+ // Check that the call is in a synthetic class.
+ List<FoundMethodSubject> outlinedMethod =
+ inspector.allClasses().stream()
+ .flatMap(clazz -> clazz.allMethods().stream())
+ .filter(
+ methodSubject ->
+ methodSubject.isSynthetic()
+ && invokesMethodWithName(methodOfInterest.getMethodName())
+ .matches(methodSubject))
+ .collect(Collectors.toList());
+ assertEquals(1, outlinedMethod.size());
+ // Assert that method invokes the outline
+ MethodSubject caller = inspector.method(method);
+ assertThat(caller, isPresent());
+ assertThat(caller, invokesMethod(outlinedMethod.get(0)));
}
- ThrowingConsumer<CodeInspector, Exception> isNotOutlinedFrom(Method method) {
- return inspector -> {
- MethodSubject caller = inspector.method(method);
- assertThat(caller, isPresent());
- assertThat(caller, invokesMethodWithName(methodOfInterest.getMethodName()));
- };
+ void isNotOutlinedFrom(Method method) {
+ MethodSubject caller = inspector.method(method);
+ assertThat(caller, isPresent());
+ assertThat(caller, invokesMethodWithName(methodOfInterest.getMethodName()));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java
new file mode 100644
index 0000000..de9685e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java
@@ -0,0 +1,154 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugar.constantdynamic;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+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 ConstantDynamicGetDeclaredMethodsTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+ }
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines(
+ "Hello, world!",
+ "myConstant",
+ "3",
+ "java.lang.invoke.MethodHandles$Lookup",
+ "java.lang.String",
+ "java.lang.Class");
+ private static final String EXPECTED_OUTPUT_R8 =
+ StringUtils.lines("Hello, world!", "No myConstant method");
+
+ private static final Class<?> MAIN_CLASS = A.class;
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
+ assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+ testForJvm()
+ .addProgramClassFileData(getTransformedClasses())
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClassFileData(getTransformedClasses())
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ // TODO(b/210485236): This should never fail.
+ .applyIf(
+ // When not desugaring the CF code requires JDK 11.
+ DesugarTestConfiguration::isNotDesugared,
+ r -> {
+ if (parameters.isCfRuntime()
+ && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
+ r.assertSuccessWithOutput(EXPECTED_OUTPUT);
+ } else {
+ r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
+ }
+ },
+ c ->
+ DesugarTestConfiguration.isDesugared(c)
+ && parameters.isDexRuntime()
+ && parameters.asDexRuntime().getVersion().isOlderThan(Version.V8_1_0),
+ r -> r.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getTransformedClasses())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(MAIN_CLASS)
+ .applyIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ b -> b.addDontWarn(MethodHandles.Lookup.class))
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT_R8);
+ }
+
+ @Test
+ public void testR8KeepBootstrapMethod() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getTransformedClasses())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(MAIN_CLASS)
+ .addKeepMethodRules(MAIN_CLASS, "myConstant(...)")
+ .applyIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ b -> b.addDontWarn(MethodHandles.Lookup.class))
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ // TODO(b/210485236): This should never fail.
+ .applyIf(
+ parameters.getDexRuntimeVersion().isOlderThan(Version.V8_1_0),
+ b -> b.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
+ b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ private byte[] getTransformedClasses() throws IOException {
+ return transformer(A.class)
+ .setVersion(CfVersion.V11)
+ .transformConstStringToConstantDynamic(
+ "condy", A.class, "myConstant", "constantName", Object.class)
+ .transform();
+ }
+
+ public static class A {
+
+ public static Object f() {
+ return "condy"; // Will be transformed to Constant_DYNAMIC.
+ }
+
+ public static void main(String[] args) {
+ System.out.println(f());
+ Method[] methods = A.class.getDeclaredMethods();
+ for (Method method : methods) {
+ if (method.getName().equals("myConstant")) {
+ System.out.println(method.getName());
+ System.out.println(method.getParameterTypes().length);
+ for (int j = 0; j < method.getParameterTypes().length; j++) {
+ System.out.println(method.getParameterTypes()[j].getName());
+ }
+ return;
+ }
+ }
+ System.out.println("No myConstant method");
+ }
+
+ private static Object myConstant(MethodHandles.Lookup lookup, String name, Class<?> type) {
+ return "Hello, world!";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicGetDeclaredMethods.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicGetDeclaredMethods.java
new file mode 100644
index 0000000..79fc834
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicGetDeclaredMethods.java
@@ -0,0 +1,193 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugar.constantdynamic;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.jacoco.JacocoClasses;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+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 JacocoConstantDynamicGetDeclaredMethods extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public static final String jacocoBootstrapMethodName = "$jacocoInit";
+
+ public static JacocoClasses testClasses;
+
+ private static final String MAIN_CLASS = TestRunner.class.getTypeName();
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines(
+ jacocoBootstrapMethodName,
+ "3",
+ "java.lang.invoke.MethodHandles$Lookup",
+ "java.lang.String",
+ "java.lang.Class");
+
+ @BeforeClass
+ public static void setUpInput() throws IOException {
+ testClasses = testClasses(getStaticTemp());
+ }
+
+ private void checkJacocoReport(Path agentOutput) throws IOException {
+ // TODO(sgjesse): Need to figure out why there is no instrumentation output for newer VMs.
+ if (parameters.isCfRuntime()
+ || parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) {
+ assertEquals(2, testClasses.generateReport(agentOutput).size());
+ } else {
+ assertFalse(Files.exists(agentOutput));
+ }
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(
+ parameters.isCfRuntime()
+ && (parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)));
+
+ // Run non-instrumented code with an agent causing on the fly instrumentation on the JVM.
+ Path output = temp.newFolder().toPath();
+ Path agentOutputOnTheFly = output.resolve("on-the-fly");
+ testForJvm()
+ .addProgramFiles(testClasses.getOriginal())
+ .enableJaCoCoAgent(ToolHelper.JACOCO_AGENT, agentOutputOnTheFly)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ checkJacocoReport(agentOutputOnTheFly);
+
+ // Run the instrumented code.
+ Path agentOutputOffline = output.resolve("offline");
+ testForJvm()
+ .addProgramFiles(testClasses.getInstrumented())
+ .configureJaCoCoAgentForOfflineInstrumentedCode(ToolHelper.JACOCO_AGENT, agentOutputOffline)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ checkJacocoReport(agentOutputOffline);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.getRuntime().isDex());
+ Path agentOutput = temp.newFolder().toPath().resolve("jacoco.exec");
+ testForD8(parameters.getBackend())
+ .addProgramFiles(testClasses.getInstrumented())
+ .addProgramFiles(ToolHelper.JACOCO_AGENT)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .runWithJaCoCo(agentOutput, parameters.getRuntime(), MAIN_CLASS)
+ // TODO(b/210485236): This should never fail.
+ .applyIf(
+ parameters.getDexRuntimeVersion().isOlderThan(Version.V8_1_0),
+ b -> b.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
+ b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ checkJacocoReport(agentOutput);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(parameters.getRuntime().isDex());
+ Path agentOutput = temp.newFolder().toPath().resolve("jacoco.exec");
+ testForR8(parameters.getBackend())
+ .addProgramFiles(testClasses.getInstrumented())
+ .addProgramFiles(ToolHelper.JACOCO_AGENT)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRules(TestRunner.class)
+ .applyIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ b -> b.addDontWarn(MethodHandles.Lookup.class))
+ .addDontWarn("java.lang.management.ManagementFactory", "javax.management.**")
+ .compile()
+ .runWithJaCoCo(agentOutput, parameters.getRuntime(), MAIN_CLASS)
+ .inspect(
+ inspector -> {
+ assertThat(
+ inspector.clazz(TestRunner.class).uniqueMethodWithName(jacocoBootstrapMethodName),
+ isAbsent());
+ })
+ .assertSuccessWithOutputLines("No " + jacocoBootstrapMethodName + " method");
+ checkJacocoReport(agentOutput);
+ }
+
+ @Test
+ public void testR8KeepingJacocoInit() throws Exception {
+ assumeTrue(parameters.getRuntime().isDex());
+ Path agentOutput = temp.newFolder().toPath().resolve("jacoco.exec");
+ testForR8(parameters.getBackend())
+ .addProgramFiles(testClasses.getInstrumented())
+ .addProgramFiles(ToolHelper.JACOCO_AGENT)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRules(TestRunner.class)
+ .addKeepRules("-keep class ** { *** " + jacocoBootstrapMethodName + "(...); }")
+ .applyIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ b -> b.addDontWarn(MethodHandles.Lookup.class))
+ .addDontWarn(
+ "java.lang.instrument.ClassFileTransformer",
+ "java.lang.management.ManagementFactory",
+ "javax.management.**")
+ .compile()
+ .runWithJaCoCo(agentOutput, parameters.getRuntime(), MAIN_CLASS)
+ // TODO(b/210485236): This should never fail.
+ .applyIf(
+ parameters.getDexRuntimeVersion().isOlderThan(Version.V8_1_0),
+ b -> b.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
+ b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ checkJacocoReport(agentOutput);
+ }
+
+ private static JacocoClasses testClasses(TemporaryFolder temp) throws IOException {
+ return new JacocoClasses(
+ transformer(TestRunner.class).setVersion(CfVersion.V11).transform(), temp);
+ }
+
+ static class TestRunner {
+
+ public static void main(String[] args) {
+ Method[] methods = TestRunner.class.getDeclaredMethods();
+ for (Method method : methods) {
+ if (method.getName().equals(jacocoBootstrapMethodName)) {
+ System.out.println(method.getName());
+ System.out.println(method.getParameterTypes().length);
+ for (int j = 0; j < method.getParameterTypes().length; j++) {
+ System.out.println(method.getParameterTypes()[j].getName());
+ }
+ return;
+ }
+ }
+ System.out.println("No " + jacocoBootstrapMethodName + " method");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
index 266745e..9c33f50 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
@@ -3,9 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.constantdynamic;
-import static com.android.tools.r8.utils.DescriptorUtils.JAVA_PACKAGE_SEPARATOR;
-import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assume.assumeTrue;
@@ -15,12 +12,10 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.jacoco.JacocoClasses;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ZipUtils;
-import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -141,60 +136,6 @@
temp);
}
- // Two sets of class files with and without JaCoCo off line instrumentation.
- private static class JacocoClasses {
- private final Path dir;
-
- private final Path originalJar;
- private final Path instrumentedJar;
-
- // Create JacocoClasses with just one class provided as bytes.
- private JacocoClasses(byte[] clazz, TemporaryFolder temp) throws IOException {
- dir = temp.newFolder().toPath();
-
- // Write the class to a .class file with package sub-directories.
- String typeName = extractClassName(clazz);
- int lastDotIndex = typeName.lastIndexOf('.');
- String pkg = typeName.substring(0, lastDotIndex);
- String baseFileName = typeName.substring(lastDotIndex + 1) + CLASS_EXTENSION;
- Path original = dir.resolve("original");
- Files.createDirectories(original);
- Path packageDir = original.resolve(pkg.replace(JAVA_PACKAGE_SEPARATOR, File.separatorChar));
- Files.createDirectories(packageDir);
- Path classFile = packageDir.resolve(baseFileName);
- Files.write(classFile, clazz);
-
- // Run offline instrumentation.
- Path instrumented = dir.resolve("instrumented");
- Files.createDirectories(instrumented);
- runJacocoInstrumentation(original, instrumented);
- originalJar = dir.resolve("original" + JAR_EXTENSION);
- ZipUtils.zip(originalJar, original);
- instrumentedJar = dir.resolve("instrumented" + JAR_EXTENSION);
- ZipUtils.zip(instrumentedJar, instrumented);
- }
-
- public Path getOriginal() {
- return originalJar;
- }
-
- public Path getInstrumented() {
- return instrumentedJar;
- }
-
- public List<String> generateReport(Path jacocoExec) throws IOException {
- Path report = dir.resolve("report.scv");
- ProcessResult result = ToolHelper.runJaCoCoReport(originalJar, jacocoExec, report);
- assertEquals(result.toString(), 0, result.exitCode);
- return Files.readAllLines(report);
- }
-
- private void runJacocoInstrumentation(Path input, Path outdir) throws IOException {
- ProcessResult result = ToolHelper.runJaCoCoInstrument(input, outdir);
- assertEquals(result.toString(), 0, result.exitCode);
- }
- }
-
static class TestRunner {
public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
index 9fe8e54..0622a2a 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
@@ -3,11 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.internal;
-import static com.android.tools.r8.utils.AssertionUtils.assertNotNull;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.core.AnyOf.anyOf;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.D8TestBuilder;
import com.android.tools.r8.D8TestCompileResult;
@@ -21,9 +20,7 @@
import com.google.common.collect.Sets;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Set;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -60,35 +57,36 @@
@Test
public void testR8Determinism() throws Exception {
- Map<String, String> idsRoundOne = new ConcurrentHashMap<>();
+ Set<String> idsRoundOne = Sets.newConcurrentHashSet();
R8TestCompileResult compileResult =
compileWithR8(
builder ->
builder.addOptionsModification(
options ->
options.testing.processingContextsConsumer =
- id -> assertNull(idsRoundOne.put(id, id))));
+ id -> assertTrue(idsRoundOne.add(id))));
compileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
- Map<String, String> idsRoundTwo = new ConcurrentHashMap<>();
+ Set<String> idsRoundTwo = Sets.newConcurrentHashSet();
R8TestCompileResult otherCompileResult =
compileWithR8(
builder ->
builder.addOptionsModification(
options ->
options.testing.processingContextsConsumer =
- id -> {
- assertNotNull(idsRoundOne.get(id));
- assertNull(idsRoundTwo.put(id, id));
- }));
+ id -> assertTrue(idsRoundTwo.add(id))));
- // Verify that the result of the two compilations was the same.
- assertEquals(
- Collections.emptySet(),
- Sets.symmetricDifference(idsRoundOne.keySet(), idsRoundTwo.keySet()));
- assertIdenticalApplications(compileResult.getApp(), otherCompileResult.getApp());
- assertEquals(compileResult.getProguardMap(), otherCompileResult.getProguardMap());
+ uploadJarsToCloudStorageIfTestFails(
+ (ignored1, ignored2) -> {
+ // Verify that the result of the two compilations was the same.
+ assertEquals(idsRoundOne, idsRoundTwo);
+ assertIdenticalApplications(compileResult.getApp(), otherCompileResult.getApp());
+ assertEquals(compileResult.getProguardMap(), otherCompileResult.getProguardMap());
+ return true;
+ },
+ compileResult.writeToZip(),
+ otherCompileResult.writeToZip());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
index 8573c7b..cc1ded1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
@@ -75,7 +75,7 @@
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("0");
+ .assertSuccessWithOutputLines("0", "0");
}
private static byte[] getTransformedMain() throws IOException {
@@ -98,6 +98,13 @@
public static void main(String[] args) {
// Transformed from `I get(int)` to `Object get(int)`.
get(args.length).m();
+
+ // Transformed from `I get(int)` to `Object get(int)`.
+ try {
+ get(args.length).m();
+ } catch (Exception e) {
+ throw new RuntimeException();
+ }
}
// @Keep
diff --git a/src/test/java/com/android/tools/r8/jacoco/JacocoClasses.java b/src/test/java/com/android/tools/r8/jacoco/JacocoClasses.java
new file mode 100644
index 0000000..b98cc63
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jacoco/JacocoClasses.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jacoco;
+
+import static com.android.tools.r8.utils.DescriptorUtils.JAVA_PACKAGE_SEPARATOR;
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.ZipUtils;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.rules.TemporaryFolder;
+
+// Two sets of class files with and without JaCoCo offline instrumentation.
+public class JacocoClasses {
+
+ private final TemporaryFolder temp;
+ private final Path dir;
+
+ private final Path originalJar;
+ private final Path instrumentedJar;
+
+ // Create JacocoClasses with just one class provided as bytes.
+ public JacocoClasses(byte[] clazz, TemporaryFolder temp) throws IOException {
+ this.temp = temp;
+ dir = temp.newFolder().toPath();
+
+ // Write the class to a .class file with package sub-directories.
+ String typeName = TestBase.extractClassName(clazz);
+ int lastDotIndex = typeName.lastIndexOf('.');
+ String pkg = typeName.substring(0, lastDotIndex);
+ String baseFileName = typeName.substring(lastDotIndex + 1) + CLASS_EXTENSION;
+ Path original = dir.resolve("original");
+ Files.createDirectories(original);
+ Path packageDir = original.resolve(pkg.replace(JAVA_PACKAGE_SEPARATOR, File.separatorChar));
+ Files.createDirectories(packageDir);
+ Path classFile = packageDir.resolve(baseFileName);
+ Files.write(classFile, clazz);
+
+ // Run offline instrumentation.
+ Path instrumented = dir.resolve("instrumented");
+ Files.createDirectories(instrumented);
+ runJacocoInstrumentation(original, instrumented);
+ originalJar = dir.resolve("original" + JAR_EXTENSION);
+ ZipUtils.zip(originalJar, original);
+ instrumentedJar = dir.resolve("instrumented" + JAR_EXTENSION);
+ ZipUtils.zip(instrumentedJar, instrumented);
+ }
+
+ public Path getOriginal() {
+ return originalJar;
+ }
+
+ public Path getInstrumented() {
+ return instrumentedJar;
+ }
+
+ public List<String> generateReport(Path jacocoExec) throws IOException {
+ Path report = temp.newFolder().toPath().resolve("report.scv");
+ ProcessResult result = ToolHelper.runJaCoCoReport(originalJar, jacocoExec, report);
+ assertEquals(result.toString(), 0, result.exitCode);
+ return Files.readAllLines(report);
+ }
+
+ private void runJacocoInstrumentation(Path input, Path outdir) throws IOException {
+ ProcessResult result = ToolHelper.runJaCoCoInstrument(input, outdir);
+ assertEquals(result.toString(), 0, result.exitCode);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
index 21c1cce..29b0027 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
@@ -121,6 +121,7 @@
.addSourceFiles(
getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
.setOutputPath(temp.newFolder().toPath())
+ .disableAssertions()
.compileRaw();
assertEquals(1, mainResult.exitCode);
assertThat(mainResult.stderr, containsString("cannot access 'LibReference'"));
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineBlockTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineBlockTest.java
new file mode 100644
index 0000000..5396f4d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineBlockTest.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata;
+
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteCrossinlineBlockTest extends KotlinMetadataTestBase {
+
+ private final String EXPECTED =
+ StringUtils.lines(
+ "foo", "42", "42", "0", "42", "42", "42", "42", "42", "42", "42", "42", "42", "42", "42",
+ "42", "42", "42", "42", "42", "42", "42", "42", "42", "42", "42", "42", "42", "42", "42",
+ "42", "42", "42", "42");
+ private static final String PKG_LIB = PKG + ".crossinline_block_lib";
+ private static final String PKG_APP = PKG + ".crossinline_block_app";
+
+ @Parameterized.Parameters(name = "{0}, {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
+ }
+
+ public MetadataRewriteCrossinlineBlockTest(
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
+ this.parameters = parameters;
+ }
+
+ private static final KotlinCompileMemoizer libJars =
+ getCompileMemoizer(
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"));
+ private final TestParameters parameters;
+
+ @Test
+ public void smokeTest() throws Exception {
+ Path libJar = libJars.getForConfiguration(kotlinc, targetVersion);
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+ testForJvm()
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testMetadataForLib() throws Exception {
+ Path libJar =
+ testForR8Compat(parameters.getBackend())
+ .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+ .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
+ .addKeepAllClassesRule()
+ .addKeepAllAttributes()
+ .compile()
+ .writeToZip();
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+ testForJvm()
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/crossinline_block_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/crossinline_block_app/main.kt
new file mode 100644
index 0000000..b349233
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/crossinline_block_app/main.kt
@@ -0,0 +1,22 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.crossinline_block_app
+
+import com.android.tools.r8.kotlin.metadata.crossinline_block_lib.bar
+import com.android.tools.r8.kotlin.metadata.crossinline_block_lib.foo
+
+fun main(args : Array<String>) {
+ if (args.isEmpty()) {
+ call("foo")
+ bar(bar3 = 0)
+ } else {
+ call("bar")
+ }
+}
+
+fun call(name: String) {
+ foo { name }
+ println(name)
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/crossinline_block_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/crossinline_block_lib/lib.kt
new file mode 100644
index 0000000..d7efaaf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/crossinline_block_lib/lib.kt
@@ -0,0 +1,82 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.crossinline_block_lib
+
+public inline fun foo(
+ bar: String? = null,
+ crossinline block: () -> String? = { null }
+) {
+ block()
+}
+
+public inline fun bar(
+ bar1: Int = 42,
+ bar2: Int = 42,
+ bar3: Int = 42,
+ bar4: Int = 42,
+ bar5: Int = 42,
+ bar6: Int = 42,
+ bar7: Int = 42,
+ bar8: Int = 42,
+ bar9: Int = 42,
+ bar10: Int = 42,
+ bar11: Int = 42,
+ bar12: Int = 42,
+ bar13: Int = 42,
+ bar14: Int = 42,
+ bar15: Int = 42,
+ bar16: Int = 42,
+ bar17: Int = 42,
+ bar18: Int = 42,
+ bar19: Int = 42,
+ bar20: Int = 42,
+ bar21: Int = 42,
+ bar22: Int = 42,
+ bar23: Int = 42,
+ bar24: Int = 42,
+ bar25: Int = 42,
+ bar26: Int = 42,
+ bar27: Int = 42,
+ bar28: Int = 42,
+ bar29: Int = 42,
+ bar30: Int = 42,
+ bar31: Int = 42,
+ bar32: Int = 42,
+ bar33: Int = 42
+) {
+ println(bar1)
+ println(bar2)
+ println(bar3)
+ println(bar4)
+ println(bar5)
+ println(bar6)
+ println(bar7)
+ println(bar8)
+ println(bar9)
+ println(bar10)
+ println(bar11)
+ println(bar12)
+ println(bar13)
+ println(bar14)
+ println(bar15)
+ println(bar16)
+ println(bar17)
+ println(bar18)
+ println(bar19)
+ println(bar20)
+ println(bar21)
+ println(bar22)
+ println(bar23)
+ println(bar24)
+ println(bar25)
+ println(bar26)
+ println(bar27)
+ println(bar28)
+ println(bar29)
+ println(bar30)
+ println(bar31)
+ println(bar32)
+ println(bar33)
+}
\ No newline at end of file
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index 440cd97..50dba8d 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -740,7 +740,16 @@
if options.print_dexsegments:
dex_files = glob(os.path.join(outdir, '*.dex'))
utils.print_dexsegments(options.print_dexsegments, dex_files)
+ if not options.golem:
+ print('{}-Total(CodeSize): {}'.format(
+ options.print_dexsegments, compute_size_of_dex_files(dex_files)))
return 0
+def compute_size_of_dex_files(dex_files):
+ dex_size = 0
+ for dex_file in dex_files:
+ dex_size += os.path.getsize(dex_file)
+ return dex_size
+
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))