| // Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| package com.android.tools.r8; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assume.assumeTrue; |
| |
| import com.android.tools.r8.TestBase.Backend; |
| import com.android.tools.r8.TestRuntime.CfRuntime; |
| import com.android.tools.r8.TestRuntime.CfVm; |
| import com.android.tools.r8.TestRuntime.DexRuntime; |
| import com.android.tools.r8.TestRuntime.NoneRuntime; |
| import com.android.tools.r8.ToolHelper.DexVm; |
| import com.android.tools.r8.utils.AndroidApiLevel; |
| import java.nio.file.Path; |
| |
| // Actual test parameters for a specific configuration. Currently just the runtime configuration. |
| public class TestParameters { |
| |
| private final TestRuntime runtime; |
| private final AndroidApiLevel apiLevel; |
| private final PartialCompilationTestParameters partialCompilationTestParameters; |
| private final boolean representativeApiLevelForRuntime; |
| |
| public TestParameters(TestRuntime runtime) { |
| this(runtime, null, PartialCompilationTestParameters.NONE); |
| } |
| |
| public TestParameters( |
| TestRuntime runtime, |
| AndroidApiLevel apiLevel, |
| PartialCompilationTestParameters partialCompilationTestParameters) { |
| this(runtime, apiLevel, partialCompilationTestParameters, true); |
| } |
| |
| public TestParameters( |
| TestRuntime runtime, |
| AndroidApiLevel apiLevel, |
| PartialCompilationTestParameters partialCompilationTestParameters, |
| boolean representativeApiLevelForRuntime) { |
| assert runtime != null; |
| this.runtime = runtime; |
| this.apiLevel = apiLevel; |
| this.partialCompilationTestParameters = partialCompilationTestParameters; |
| this.representativeApiLevelForRuntime = representativeApiLevelForRuntime; |
| } |
| |
| public static TestParametersBuilder builder() { |
| return new TestParametersBuilder(); |
| } |
| |
| public static TestParametersCollection justNoneRuntime() { |
| return builder().withNoneRuntime().build(); |
| } |
| |
| public boolean canHaveIssueWithInlinedMonitors() { |
| return isCfRuntime() || getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.M); |
| } |
| |
| /** |
| * Returns true if the runtime uses resolution to lookup the constructor targeted by a given |
| * invoke, so that it is valid to have non-rebound constructor invokes. |
| * |
| * <p>Example: If value `v` is an uninitialized instanceof type `T`, then calling `T.<init>()` |
| * succeeds on ART even if `T.<init>()` does not exists, as ART will resolve the constructor, find |
| * `Object.<init>()`, and use this method to initialize `v`. On the JVM and on Dalvik, this is a |
| * runtime error. |
| */ |
| public boolean canHaveNonReboundConstructorInvoke() { |
| return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L); |
| } |
| |
| public boolean canInitNewInstanceUsingSuperclassConstructor() { |
| return canHaveNonReboundConstructorInvoke(); |
| } |
| |
| public boolean canUseDefaultAndStaticInterfaceMethods() { |
| assert isCfRuntime() || isDexRuntime(); |
| assert !isCfRuntime() || apiLevel == null |
| : "Use canUseDefaultAndStaticInterfaceMethodsWhenDesugaring when using CF api levels."; |
| return isCfRuntime() |
| || getApiLevel() |
| .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport()); |
| } |
| |
| public boolean canUseDefaultAndStaticInterfaceMethodsWhenDesugaring() { |
| assert isCfRuntime() || isDexRuntime(); |
| assert apiLevel != null; |
| return getApiLevel() |
| .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport()); |
| } |
| |
| public boolean canUseJavaLangInvokeVarHandleStoreStoreFence() { |
| return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.T); |
| } |
| |
| public boolean canUseNativeDexPC() { |
| assert isCfRuntime() || isDexRuntime(); |
| return isDexRuntime() && getDexRuntimeVersion().isNewerThanOrEqual(DexVm.Version.V8_1_0); |
| } |
| |
| public boolean canUseNativeMultidex() { |
| return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L); |
| } |
| |
| public boolean canUseNestBasedAccesses() { |
| assert isCfRuntime() || isDexRuntime(); |
| return isCfRuntime() && getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11); |
| } |
| |
| public boolean canUseNestBasedAccessesWhenDesugaring() { |
| assert isCfRuntime() || isDexRuntime(); |
| assert apiLevel != null; |
| return false; |
| } |
| |
| public boolean canUseFilledNewArrayOnNonStringObjects() { |
| return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N); |
| } |
| |
| public boolean canUseSubTypesInFilledNewArray() { |
| return isDexRuntime() && getApiLevel().isGreaterThan(AndroidApiLevel.U); |
| } |
| |
| public boolean isAccessModificationEnabled(boolean allowAccessModification) { |
| return allowAccessModification || isAccessModificationEnabledByDefault(); |
| } |
| |
| public boolean isAccessModificationEnabledByDefault() { |
| return true; |
| } |
| |
| public boolean runtimeWithClassValue() { |
| assert isCfRuntime() || isDexRuntime(); |
| return isCfRuntime() || getDexRuntimeVersion().isNewerThanOrEqual(DexVm.Version.V14_0_0); |
| } |
| |
| // Convenience predicates. |
| public boolean isDexRuntime() { |
| return runtime.isDex(); |
| } |
| |
| public DexRuntime asDexRuntime() { |
| return getRuntime().asDex(); |
| } |
| |
| public boolean isCfRuntime() { |
| return runtime.isCf(); |
| } |
| |
| public boolean isCfRuntime(CfVm vm) { |
| return runtime.isCf() && runtime.asCf().getVm() == vm; |
| } |
| |
| public CfRuntime asCfRuntime() { |
| return getRuntime().asCf(); |
| } |
| |
| public boolean isDexRuntimeVersion(DexVm.Version vm) { |
| return isDexRuntime() && getDexRuntimeVersion().isEqualTo(vm); |
| } |
| |
| public boolean isDexRuntimeVersionNewerThanOrEqual(DexVm.Version vm) { |
| return isDexRuntime() && getDexRuntimeVersion().isNewerThanOrEqual(vm); |
| } |
| |
| public boolean isDexRuntimeVersionOlderThanOrEqual(DexVm.Version vm) { |
| return isDexRuntime() && getDexRuntimeVersion().isOlderThanOrEqual(vm); |
| } |
| |
| public boolean isNoneRuntime() { |
| return runtime == NoneRuntime.getInstance(); |
| } |
| |
| public void configureApiLevel(TestAppViewBuilder testAppViewBuilder) { |
| testAppViewBuilder.setMinApi(apiLevel); |
| } |
| |
| public void configureApiLevel(TestCompilerBuilder<?, ?, ?, ?, ?> testCompilerBuilder) { |
| testCompilerBuilder.setMinApi(apiLevel); |
| } |
| |
| // TODO(b/270021825): Tests should not access the underlying API level directly, but may be |
| // allowed to query if the api level satisfies some condition. |
| @Deprecated |
| public AndroidApiLevel getApiLevel() { |
| if (runtime.isDex() && apiLevel == null) { |
| throw new RuntimeException( |
| "Use of getApiLevel without configured API levels for TestParametersCollection."); |
| } |
| return apiLevel; |
| } |
| |
| public Path getDefaultAndroidJar() { |
| assert isDexRuntime(); |
| return ToolHelper.getFirstSupportedAndroidJar(getApiLevel()); |
| } |
| |
| public Path getDefaultAndroidJarAbove(AndroidApiLevel minimumCompileApiLevel) { |
| assert isDexRuntime(); |
| return ToolHelper.getFirstSupportedAndroidJar(getApiLevel().max(minimumCompileApiLevel)); |
| } |
| |
| public Path getDefaultRuntimeLibrary() { |
| return isCfRuntime() ? ToolHelper.getJava8RuntimeJar() : getDefaultAndroidJar(); |
| } |
| |
| public CfRuntime getCfRuntime() { |
| assert isCfRuntime(); |
| return runtime.asCf(); |
| } |
| |
| public PartialCompilationTestParameters getPartialCompilationTestParameters() { |
| return partialCompilationTestParameters; |
| } |
| |
| // Access to underlying runtime/wrapper. |
| public TestRuntime getRuntime() { |
| return runtime; |
| } |
| |
| public boolean isOrSimulateNoneRuntime() { |
| return isNoneRuntime() |
| || (runtime != null && runtime.equals(TestRuntime.getDefaultCfRuntime())); |
| } |
| |
| // Helper function to get the "backend" for a given runtime target. |
| public Backend getBackend() { |
| return runtime.getBackend(); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(runtime.toString()); |
| if (apiLevel != null) { |
| builder.append(", api:").append(apiLevel.getLevel()); |
| } |
| if (partialCompilationTestParameters.isSome()) { |
| builder.append(", partial:").append(partialCompilationTestParameters); |
| } |
| return builder.toString(); |
| } |
| |
| public void assertCfRuntime() { |
| assertTrue(runtime.isCf()); |
| } |
| |
| public void assertNoneRuntime() { |
| assertEquals(NoneRuntime.getInstance(), runtime); |
| } |
| |
| public void assertIsRepresentativeApiLevelForRuntime() { |
| assertTrue(representativeApiLevelForRuntime); |
| } |
| |
| public TestParameters assumeCfRuntime() { |
| assumeTrue(isCfRuntime()); |
| return this; |
| } |
| |
| public TestParameters assumeDexRuntime() { |
| assumeTrue(isDexRuntime()); |
| return this; |
| } |
| |
| public TestParameters assumeIsOrSimulateNoneRuntime() { |
| assumeTrue(isOrSimulateNoneRuntime()); |
| return this; |
| } |
| |
| public boolean isJvmTestParameters() { |
| if (isCfRuntime()) { |
| return apiLevel == null || representativeApiLevelForRuntime; |
| } |
| return false; |
| } |
| |
| public TestParameters assumeJvmTestParameters() { |
| assumeCfRuntime(); |
| assumeTrue(apiLevel == null || representativeApiLevelForRuntime); |
| return this; |
| } |
| |
| public TestParameters assumeProguardTestParameters() { |
| assumeCfRuntime(); |
| assumeTrue(apiLevel == null || representativeApiLevelForRuntime); |
| return this; |
| } |
| |
| public TestParameters assumeR8TestParameters() { |
| assumeTrue(isR8TestParameters()); |
| return this; |
| } |
| |
| public boolean isR8TestParameters() { |
| assertFalse( |
| "No need to use is/assumeR8TestParameters() when not using api levels for CF", |
| isCfRuntime() && apiLevel == null); |
| assertTrue(apiLevel != null || representativeApiLevelForRuntime); |
| return (isDexRuntime() || representativeApiLevelForRuntime) && !isNoneRuntime(); |
| } |
| |
| public TestParameters assumeR8PartialTestParameters() { |
| assumeTrue(isR8PartialTestParameters()); |
| return this; |
| } |
| |
| public boolean isR8PartialTestParameters() { |
| return isDexRuntime() && apiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L); |
| } |
| |
| public TestParameters assumeRuntimeTestParameters() { |
| assertFalse( |
| "No need to use assumeRuntimeTestParameters() when not using api levels for CF", |
| apiLevel == null); |
| assumeTrue(isDexRuntime() || representativeApiLevelForRuntime); |
| return this; |
| } |
| |
| public DexVm.Version getDexRuntimeVersion() { |
| assertTrue(isDexRuntime()); |
| return getRuntime().asDex().getVm().getVersion(); |
| } |
| |
| TestParameters withPartialCompilationTestParameters( |
| PartialCompilationTestParameters partialCompilationTestParameters) { |
| assert getPartialCompilationTestParameters().isNone(); |
| return new TestParameters( |
| runtime, apiLevel, partialCompilationTestParameters, representativeApiLevelForRuntime); |
| } |
| } |