VarHandle desugaring: Library desugaring of ConcurrentLinkedQueue
Enable VarHandle desugaring in L8 and add ConcurrentLinkedQueue to all
JDK11 based desugared library configurations.
Bug: b/246860430
Bug: b/247076137
Change-Id: Id462e53e33bf229bcda7758e4817073b9dcdc27e
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index f3d0764..d6649f7 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -24,7 +24,8 @@
{
"api_level_below_or_equal": 32,
"rewrite_prefix": {
- "java.util.concurrent.DesugarTimeUnit": "j$.util.concurrent.DesugarTimeUnit"
+ "java.util.concurrent.DesugarTimeUnit": "j$.util.concurrent.DesugarTimeUnit",
+ "java.util.concurrent.ConcurrentLinkedQueue": "j$.util.concurrent.ConcurrentLinkedQueue"
},
"retarget_method": {
"java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)": "java.util.concurrent.DesugarTimeUnit",
@@ -300,6 +301,12 @@
],
"library_flags": [
{
+ "api_level_below_or_equal": 32,
+ "rewrite_prefix": {
+ "java.util.concurrent.Helpers": "j$.util.concurrent.Helpers"
+ }
+ },
+ {
"api_level_below_or_equal": 30,
"rewrite_prefix": {
"jdk.internal.": "j$.jdk.internal.",
@@ -342,7 +349,6 @@
"java.util.KeyValueHolder": "j$.util.KeyValueHolder",
"java.util.SortedSet$1": "j$.util.SortedSet$1",
"java.util.Tripwire": "j$.util.Tripwire",
- "java.util.concurrent.Helpers": "j$.util.concurrent.Helpers",
"java.util.ConversionRuntimeException": "j$.util.ConversionRuntimeException"
},
"rewrite_derived_prefix": {
@@ -381,6 +387,9 @@
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$TreeBin { int lockState; }",
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap { int sizeCtl; int transferIndex; long baseCount; int cellsBusy; }",
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$CounterCell { long value; }",
+ "-keepclassmembers class j$.util.concurrent.ConcurrentLinkedQueue { j$.util.concurrent.ConcurrentLinkedQueue$Node head; j$.util.concurrent.ConcurrentLinkedQueue$Node tail; }",
+ "-keep,allowshrinking class j$.util.concurrent.ConcurrentLinkedQueue$Node",
+ "-keepclassmembers class j$.util.concurrent.ConcurrentLinkedQueue$Node { j$.util.concurrent.ConcurrentLinkedQueue$Node next; java.lang.Object item; }",
"-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); public static final !synthetic <fields>; }",
"-keeppackagenames java.**",
"-keeppackagenames j$.**",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
index 119b674..eb7db99 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
@@ -6,6 +6,12 @@
"support_all_callbacks_from_library": false,
"common_flags": [
{
+ "api_level_below_or_equal": 32,
+ "rewrite_prefix": {
+ "java.util.concurrent.ConcurrentLinkedQueue": "j$.util.concurrent.ConcurrentLinkedQueue"
+ }
+ },
+ {
"api_level_below_or_equal": 23,
"maintain_prefix": [
"java.util.function.",
@@ -14,8 +20,19 @@
}
],
"program_flags": [],
- "library_flags": [],
+ "library_flags": [
+ {
+ "api_level_below_or_equal": 32,
+ "rewrite_prefix": {
+ "java.util.concurrent.Helpers": "j$.util.concurrent.Helpers"
+ }
+ }
+ ],
"shrinker_config": [
+ "-keepclassmembers class j$.** extends java.io.Serializable { void <init>(); private static final java.io.ObjectStreamField[] serialPersistentFields; static final long serialVersionUID; java.lang.Object readResolve(); java.lang.Object writeReplace(); private void readObject(java.io.ObjectInputStream); private void writeObject(java.io.ObjectOutputStream); private void readObjectNoData(); }",
+ "-keepclassmembers class j$.util.concurrent.ConcurrentLinkedQueue { j$.util.concurrent.ConcurrentLinkedQueue$Node head; j$.util.concurrent.ConcurrentLinkedQueue$Node tail; }",
+ "-keep,allowshrinking class j$.util.concurrent.ConcurrentLinkedQueue$Node",
+ "-keepclassmembers class j$.util.concurrent.ConcurrentLinkedQueue$Node { j$.util.concurrent.ConcurrentLinkedQueue$Node next; java.lang.Object item; }",
"-keeppackagenames java.**",
"-keeppackagenames j$.**",
"-keepattributes Signature",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index 5269d86..8a40f4c 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -34,7 +34,8 @@
"java.net.URLDecoder": "j$.net.URLDecoder",
"java.net.URLEncoder": "j$.net.URLEncoder",
"java.io.DesugarInputStream": "j$.io.DesugarInputStream",
- "java.util.concurrent.DesugarTimeUnit": "j$.util.concurrent.DesugarTimeUnit"
+ "java.util.concurrent.DesugarTimeUnit": "j$.util.concurrent.DesugarTimeUnit",
+ "java.util.concurrent.ConcurrentLinkedQueue": "j$.util.concurrent.ConcurrentLinkedQueue"
},
"retarget_method": {
"java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)": "java.util.concurrent.DesugarTimeUnit",
@@ -476,7 +477,8 @@
"rewrite_prefix": {
"desugar.": "j$.desugar.",
"libcore.": "j$.libcore.",
- "sun.security.action.": "j$.sun.security.action."
+ "sun.security.action.": "j$.sun.security.action.",
+ "java.util.concurrent.Helpers": "j$.util.concurrent.Helpers"
}
},
{
@@ -588,6 +590,9 @@
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$TreeBin { int lockState; }",
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap { int sizeCtl; int transferIndex; long baseCount; int cellsBusy; }",
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$CounterCell { long value; }",
+ "-keepclassmembers class j$.util.concurrent.ConcurrentLinkedQueue { j$.util.concurrent.ConcurrentLinkedQueue$Node head; j$.util.concurrent.ConcurrentLinkedQueue$Node tail; }",
+ "-keep,allowshrinking class j$.util.concurrent.ConcurrentLinkedQueue$Node",
+ "-keepclassmembers class j$.util.concurrent.ConcurrentLinkedQueue$Node { j$.util.concurrent.ConcurrentLinkedQueue$Node next; java.lang.Object item; }",
"-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); public static final !synthetic <fields>; }",
"-keeppackagenames java.**",
"-keeppackagenames j$.**",
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index aea1c45..1acbd0c 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -294,13 +294,6 @@
appView.setNamingLens(
RecordRewritingNamingLens.createRecordRewritingNamingLens(appView)));
- timing.time(
- "Create MethodHandle.Lookup rewriting lens",
- () ->
- appView.setNamingLens(
- VarHandleDesugaringRewritingNamingLens
- .createVarHandleDesugaringRewritingNamingLens(appView)));
-
if (options.isGeneratingDex()
&& hasDexResources
&& hasClassResources
@@ -336,6 +329,14 @@
finalizeApplication(appView, executor, timing);
+ // Add the VarHandle naming lens after synthetic finalization.
+ timing.time(
+ "Create MethodHandle.Lookup rewriting lens",
+ () ->
+ appView.setNamingLens(
+ VarHandleDesugaringRewritingNamingLens
+ .createVarHandleDesugaringRewritingNamingLens(appView)));
+
timing.end(); // post-converter
if (options.isGeneratingClassFiles()) {
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 1dddf3f..56558ed 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.naming.PrefixRewritingNamingLens;
+import com.android.tools.r8.naming.VarHandleDesugaringRewritingNamingLens;
import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.shaking.AnnotationRemover;
@@ -102,12 +103,15 @@
options.enableSwitchRewriting = false;
assert options.enableStringSwitchConversion;
options.enableStringSwitchConversion = false;
+ assert !options.enableVarHandleDesugaring;
+ options.enableVarHandleDesugaring = true;
desugar(app, options, executorService);
options.forceAnnotateSynthetics = false;
options.enableSwitchRewriting = true;
options.enableStringSwitchConversion = true;
+ options.enableVarHandleDesugaring = false;
});
if (shrink) {
R8.run(r8Command, executorService);
@@ -140,6 +144,9 @@
SyntheticFinalization.finalize(appView, timing, executor);
appView.setNamingLens(PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView));
+ appView.setNamingLens(
+ VarHandleDesugaringRewritingNamingLens.createVarHandleDesugaringRewritingNamingLens(
+ appView));
new GenericSignatureRewriter(appView).run(appView.appInfo().classes(), executor);
new CfApplicationWriter(appView, options.getMarker(Tool.L8))
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 49bfc86..311a287 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -219,6 +219,25 @@
assert newProgramClasses != null;
this.programClasses.clear();
this.programClasses.addAll(newProgramClasses);
+
+ DexApplicationReadFlags.Builder builder = DexApplicationReadFlags.builder();
+ builder.setHasReadProgramClassFromDex(this.flags.hasReadProgramClassFromDex());
+ builder.setHasReadProgramClassFromCf(this.flags.hasReadProgramClassFromCf());
+ this.programClasses.forEach(
+ clazz -> {
+ DexType type = clazz.getType();
+ if (flags.getRecordWitnesses().contains(type)) {
+ builder.addRecordWitness(type);
+ }
+ if (flags.getVarHandleWitnesses().contains(type)) {
+ builder.addVarHandleWitness(type);
+ }
+ if (flags.getMethodHandlesLookupWitnesses().contains(type)) {
+ builder.addMethodHandlesLookupWitness(type);
+ }
+ });
+ this.flags = builder.build();
+
return self();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 4df3ab0..1ba1122 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -80,6 +80,8 @@
public static final String varHandleDescriptorString = "Ljava/lang/invoke/VarHandle;";
public static final String desugarMethodHandlesLookupDescriptorString =
"Lcom/android/tools/r8/DesugarMethodHandlesLookup;";
+ public static final String methodHandlesLookupDescriptorString =
+ "Ljava/lang/invoke/MethodHandles$Lookup;";
public static final String dalvikAnnotationOptimizationPrefixString =
"Ldalvik/annotation/optimization/";
@@ -275,7 +277,7 @@
public final DexString methodHandleDescriptor = createString("Ljava/lang/invoke/MethodHandle;");
public final DexString methodHandlesDescriptor = createString("Ljava/lang/invoke/MethodHandles;");
public final DexString methodHandlesLookupDescriptor =
- createString("Ljava/lang/invoke/MethodHandles$Lookup;");
+ createString(methodHandlesLookupDescriptorString);
public final DexString methodTypeDescriptor = createString("Ljava/lang/invoke/MethodType;");
public final DexString invocationHandlerDescriptor =
createString("Ljava/lang/reflect/InvocationHandler;");
@@ -2708,9 +2710,10 @@
if (result == null) {
result = new DexType(descriptor);
assert result.isArrayType()
- || result.isClassType()
- || result.isPrimitiveType()
- || result.isVoidType();
+ || result.isClassType()
+ || result.isPrimitiveType()
+ || result.isVoidType()
+ : descriptor.toString();
assert !isInternalSentinel(result);
types.put(descriptor, result);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 42497b9..dc31133 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -626,6 +626,14 @@
int limit = 11;
for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
if (!clazz.type.descriptor.startsWith(neverMergePrefix)) {
+ boolean hasExceptionPrefix = false;
+ for (DexString exceptionPrefix : neverMerge.getExceptionPrefixes()) {
+ hasExceptionPrefix =
+ hasExceptionPrefix | clazz.type.descriptor.startsWith(exceptionPrefix);
+ }
+ if (hasExceptionPrefix) {
+ continue;
+ }
if (limit-- < 0) {
message.append("..");
break;
diff --git a/src/main/java/com/android/tools/r8/naming/VarHandleDesugaringRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/VarHandleDesugaringRewritingNamingLens.java
index cc9c83f..0d7078f 100644
--- a/src/main/java/com/android/tools/r8/naming/VarHandleDesugaringRewritingNamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/VarHandleDesugaringRewritingNamingLens.java
@@ -13,6 +13,8 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.IdentityHashMap;
+import java.util.Map;
// Naming lens for VarHandle desugaring rewriting. Rewriting java.lang.invoke.MethodHandles$Lookup
// to com.android.tools.r8.DesugarMethodHandlesLookup.
@@ -20,26 +22,80 @@
private final DexItemFactory factory;
private final NamingLens namingLens;
+ private final Map<DexType, DexString> mapping;
public static NamingLens createVarHandleDesugaringRewritingNamingLens(AppView<?> appView) {
+ DexItemFactory factory = appView.dexItemFactory();
if (appView.options().shouldDesugarVarHandle()
- && (appView
- .appInfo()
- .definitionForWithoutExistenceAssert(appView.dexItemFactory().lookupType)
- != null
- || appView
- .appInfo()
- .definitionForWithoutExistenceAssert(appView.dexItemFactory().varHandleType)
+ && (appView.appInfo().definitionForWithoutExistenceAssert(factory.lookupType) != null
+ || appView.appInfo().definitionForWithoutExistenceAssert(factory.varHandleType)
!= null)) {
- return new VarHandleDesugaringRewritingNamingLens(appView);
+
+ // Prune all inner classes attributes referring to MethodHandles$Lookup, as that is rewritten
+ // to the toplevel class DesugarMethodHandlesLookup.
+ appView
+ .appInfo()
+ .classes()
+ .forEach(
+ clazz -> {
+ clazz.removeInnerClasses(
+ innerClassAttribute -> innerClassAttribute.getInner() == factory.lookupType);
+ });
+
+ // Function to prefix type namespace, e.g. rename L... to Lj$/...
+ Map<DexType, DexString> mapping = new IdentityHashMap<>();
+ addRewritingForGlobalSynthetic(
+ appView, factory.lookupType, factory.desugarMethodHandlesLookupType, mapping);
+ addRewritingForGlobalSynthetic(
+ appView, factory.varHandleType, factory.desugarVarHandleType, mapping);
+ return new VarHandleDesugaringRewritingNamingLens(appView, mapping);
}
return appView.getNamingLens();
}
- public VarHandleDesugaringRewritingNamingLens(AppView<?> appView) {
+ private static void addRewritingForGlobalSynthetic(
+ AppView<?> appView,
+ DexType globalSynthetic,
+ DexType desugaredGlobalSynthetic,
+ Map<DexType, DexString> mapping) {
+ DexItemFactory factory = appView.dexItemFactory();
+ // The VarHandle global synthetics and synthetics derived from them are rewritten to use the
+ // desugared name.
+ assert appView.appInfo().getSyntheticItems().isFinalized();
+ String globalSyntheticString = globalSynthetic.descriptor.toString();
+ DexString currentPrefix =
+ factory.createString(
+ globalSyntheticString.substring(0, globalSyntheticString.length() - 1));
+ String desugaredGlobalSyntheticString = desugaredGlobalSynthetic.descriptor.toString();
+ DexString newPrefix =
+ appView.options().synthesizedClassPrefix.isEmpty()
+ ? factory.createString(
+ "L"
+ + desugaredGlobalSyntheticString.substring(
+ 1, desugaredGlobalSyntheticString.length() - 1))
+ : factory.createString(
+ "L"
+ + appView.options().synthesizedClassPrefix
+ + desugaredGlobalSyntheticString.substring(
+ 1, desugaredGlobalSyntheticString.length() - 1));
+ // Rewrite the global synthetic in question and all the synthetics derived from it.
+ appView
+ .appInfo()
+ .getSyntheticItems()
+ .collectSyntheticsFromContext(globalSynthetic)
+ .forEach(
+ synthetic ->
+ mapping.put(
+ synthetic,
+ synthetic.descriptor.withNewPrefix(currentPrefix, newPrefix, factory)));
+ }
+
+ private VarHandleDesugaringRewritingNamingLens(
+ AppView<?> appView, Map<DexType, DexString> mapping) {
super(appView.dexItemFactory());
this.factory = appView.dexItemFactory();
this.namingLens = appView.getNamingLens();
+ this.mapping = mapping;
}
private boolean isRenamed(DexType type) {
@@ -49,12 +105,7 @@
private DexString getRenaming(DexType type) {
assert type != factory.desugarMethodHandlesLookupType;
assert type != factory.desugarVarHandleType;
- if (type == factory.lookupType) {
- return factory.desugarMethodHandlesLookupType.descriptor;
- } else if (type == factory.varHandleType) {
- return factory.desugarVarHandleType.descriptor;
- }
- return null;
+ return mapping.get(type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 4ea11dc..bf4029e 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -49,6 +49,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -206,6 +207,33 @@
private final ContextsForGlobalSynthetics globalContexts;
private final GlobalSyntheticsStrategy globalSyntheticsStrategy;
+ public Set<DexType> collectSyntheticsFromContext(DexType context) {
+ Set<DexType> result = Sets.newIdentityHashSet();
+ committed
+ .getMethods()
+ .forEach(
+ (synthetic, methodReferences) -> {
+ methodReferences.forEach(
+ methodReference -> {
+ if (methodReference.getContext().getSynthesizingContextType() == context) {
+ result.add(synthetic);
+ }
+ });
+ });
+ committed
+ .getClasses()
+ .forEach(
+ (synthetic, classReferences) -> {
+ classReferences.forEach(
+ classReference -> {
+ if (classReference.getContext().getSynthesizingContextType() == context) {
+ result.add(synthetic);
+ }
+ });
+ });
+ return result;
+ }
+
public SyntheticNaming getNaming() {
return naming;
}
diff --git a/src/test/java/com/android/tools/r8/L8TestBuilder.java b/src/test/java/com/android/tools/r8/L8TestBuilder.java
index 7d2ea31..b299021 100644
--- a/src/test/java/com/android/tools/r8/L8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/L8TestBuilder.java
@@ -32,6 +32,7 @@
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
public class L8TestBuilder {
@@ -247,6 +248,7 @@
// in the vanilla desugared library.
// Vanilla desugared library compilation should have no warnings.
assertTrue(
+ warnings.stream().map(Diagnostic::getDiagnosticMessage).collect(Collectors.joining()),
warnings.isEmpty()
|| warnings.stream()
.allMatch(warn -> warn.getDiagnosticMessage().contains("org.testng.Assert")));
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java
index 833e949..cb6c812 100644
--- a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java
@@ -111,7 +111,7 @@
// forwarding of Unsafe.compareAndSwapObject.
MethodReference firstBackportFromDesugarVarHandle =
SyntheticItemsTestUtils.syntheticBackportWithForwardingMethod(
- Reference.classFromDescriptor("Ljava/lang/invoke/VarHandle;"),
+ Reference.classFromDescriptor("Lcom/android/tools/r8/DesugarVarHandle;"),
0,
Reference.method(
Reference.classFromDescriptor("Lsun/misc/Unsafe;"),
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentLinkedQueueTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentLinkedQueueTest.java
new file mode 100644
index 0000000..b4028c9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentLinkedQueueTest.java
@@ -0,0 +1,149 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.SPECIFICATIONS_WITH_CF2CF;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_MINIMAL;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+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 ConcurrentLinkedQueueTest extends DesugaredLibraryTestBase {
+
+ @Parameter(0)
+ public static TestParameters parameters;
+
+ @Parameter(1)
+ public static LibraryDesugaringSpecification libraryDesugaringSpecification;
+
+ @Parameter(2)
+ public static CompilationSpecification compilationSpecification;
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ // TODO(134732760): Support Dalvik VMs, currently fails because libjavacrypto is required
+ // and present only in ART runtimes.
+ getTestParameters()
+ .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+ .withAllApiLevels()
+ .build(),
+ ImmutableList.of(JDK11_MINIMAL, JDK11, JDK11_PATH),
+ SPECIFICATIONS_WITH_CF2CF);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ // Right now we only expect one backport coming out of DesugarVarHandle - the backport with
+ // forwarding of Unsafe.compareAndSwapObject.
+ MethodReference firstBackportFromDesugarVarHandle =
+ SyntheticItemsTestUtils.syntheticBackportWithForwardingMethod(
+ Reference.classFromDescriptor("Lj$/com/android/tools/r8/DesugarVarHandle;"),
+ 0,
+ Reference.method(
+ Reference.classFromDescriptor("Lsun/misc/Unsafe;"),
+ "compareAndSwapObject",
+ ImmutableList.of(
+ Reference.typeFromDescriptor("Ljava/lang/Object;"),
+ Reference.LONG,
+ Reference.typeFromDescriptor("Ljava/lang/Object;"),
+ Reference.typeFromDescriptor("Ljava/lang/Object;")),
+ Reference.BOOL));
+
+ assertThat(
+ inspector.clazz(
+ DescriptorUtils.descriptorToJavaType(DexItemFactory.varHandleDescriptorString)),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ DescriptorUtils.descriptorToJavaType(
+ DexItemFactory.methodHandlesLookupDescriptorString)),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ "j$." + DescriptorUtils.descriptorToJavaType(DexItemFactory.varHandleDescriptorString)),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ "j$."
+ + DescriptorUtils.descriptorToJavaType(
+ DexItemFactory.methodHandlesLookupDescriptorString)),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ DescriptorUtils.descriptorToJavaType(DexItemFactory.desugarVarHandleDescriptorString)),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ DescriptorUtils.descriptorToJavaType(
+ DexItemFactory.desugarMethodHandlesLookupDescriptorString)),
+ not(isPresent()));
+
+ boolean usesNativeVarHandle =
+ parameters.asDexRuntime().getVersion().isNewerThanOrEqual(Version.V13_0_0)
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.T);
+ assertThat(
+ inspector.clazz(
+ "j$."
+ + DescriptorUtils.descriptorToJavaType(
+ DexItemFactory.desugarVarHandleDescriptorString)),
+ usesNativeVarHandle ? not(isPresent()) : isPresent());
+ assertThat(
+ inspector.clazz(firstBackportFromDesugarVarHandle.getHolderClass()),
+ usesNativeVarHandle ? not(isPresent()) : isPresent());
+ // Currently DesugarMethodHandlesLookup this is fully inlined by R8.
+ assertThat(
+ inspector.clazz(
+ "j$."
+ + DescriptorUtils.descriptorToJavaType(
+ DexItemFactory.desugarMethodHandlesLookupDescriptorString)),
+ usesNativeVarHandle || compilationSpecification.isL8Shrink()
+ ? not(isPresent())
+ : isPresent());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Executor.class)
+ .compile()
+ .inspectL8(this::inspect)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ static class Executor {
+
+ public static void main(String[] args) {
+ Queue<String> queue = new ConcurrentLinkedQueue<>();
+ queue.add("Hello, world!");
+ System.out.println(queue.poll());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentLinkedQueueTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentLinkedQueueTests.java
index a711203..c7d7c3a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentLinkedQueueTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentLinkedQueueTests.java
@@ -13,7 +13,11 @@
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestParameters;
@@ -24,9 +28,16 @@
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.DesugaredLibraryTestCompileResult;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
@@ -71,7 +82,7 @@
.withApiLevel(AndroidApiLevel.N)
.build(),
ImmutableList.of(JDK11_MINIMAL, JDK11, JDK11_PATH),
- ImmutableList.of(D8_L8DEBUG, D8_L8SHRINK));
+ ImmutableSet.of(D8_L8DEBUG, D8_L8SHRINK));
}
@BeforeClass
@@ -88,33 +99,81 @@
new Path[] {jdk11MathTestsDir.resolve(WHITEBOX + CLASS_EXTENSION)};
}
- private static void ranWithSuccessOrFailures(String testName, SingleTestRunResult result) {
- // Tests use ThreadLocalRandom, so success or failure is random. Note this is only for
- // VMs where the internal implementation is not based on JDK11.
- assertTrue(
- result.getStdOut().contains(StringUtils.lines(testName + ": SUCCESS"))
- || result
- .getStdOut()
- .contains(StringUtils.lines("Tests result in " + testName + ": FAILURE")));
- if (result.getStdOut().contains(StringUtils.lines(testName + ": SUCCESS"))) {
- assertTrue(
- result.toString(),
- result.getStdOut().contains("Total tests run: 37, Failures: 0, Skips: 0"));
- } else {
- assertTrue(
- result.toString(),
- result.getStdOut().contains("Total tests run: 37, Failures: 1, Skips: 0")
- || result.getStdOut().contains("Total tests run: 37, Failures: 2, Skips: 0")
- || result.getStdOut().contains("Total tests run: 37, Failures: 3, Skips: 0")
- || result.getStdOut().contains("Total tests run: 37, Failures: 4, Skips: 0")
- || result.getStdOut().contains("Total tests run: 37, Failures: 5, Skips: 0")
- || result.getStdOut().contains("Total tests run: 37, Failures: 6, Skips: 0")
- || result.getStdOut().contains("Total tests run: 37, Failures: 7, Skips: 0")
- || result.getStdOut().contains("Total tests run: 37, Failures: 8, Skips: 0"));
- }
+ private void inspect(CodeInspector inspector) {
+ // Right now we only expect one backport coming out of DesugarVarHandle - the backport with
+ // forwarding of Unsafe.compareAndSwapObject.
+ MethodReference firstBackportFromDesugarVarHandle =
+ SyntheticItemsTestUtils.syntheticBackportWithForwardingMethod(
+ Reference.classFromDescriptor("Lj$/com/android/tools/r8/DesugarVarHandle;"),
+ 0,
+ Reference.method(
+ Reference.classFromDescriptor("Lsun/misc/Unsafe;"),
+ "compareAndSwapObject",
+ ImmutableList.of(
+ Reference.typeFromDescriptor("Ljava/lang/Object;"),
+ Reference.LONG,
+ Reference.typeFromDescriptor("Ljava/lang/Object;"),
+ Reference.typeFromDescriptor("Ljava/lang/Object;")),
+ Reference.BOOL));
+
+ assertThat(
+ inspector.clazz(
+ DescriptorUtils.descriptorToJavaType(DexItemFactory.varHandleDescriptorString)),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ DescriptorUtils.descriptorToJavaType(
+ DexItemFactory.methodHandlesLookupDescriptorString)),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ "j$." + DescriptorUtils.descriptorToJavaType(DexItemFactory.varHandleDescriptorString)),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ "j$."
+ + DescriptorUtils.descriptorToJavaType(
+ DexItemFactory.methodHandlesLookupDescriptorString)),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ DescriptorUtils.descriptorToJavaType(DexItemFactory.desugarVarHandleDescriptorString)),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ DescriptorUtils.descriptorToJavaType(
+ DexItemFactory.desugarMethodHandlesLookupDescriptorString)),
+ not(isPresent()));
+
+ boolean usesNativeVarHandle =
+ parameters.asDexRuntime().getVersion().isNewerThanOrEqual(Version.V13_0_0)
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.T);
+ assertThat(
+ inspector.clazz(
+ "j$."
+ + DescriptorUtils.descriptorToJavaType(
+ DexItemFactory.desugarVarHandleDescriptorString)),
+ usesNativeVarHandle ? not(isPresent()) : isPresent());
+ assertThat(
+ inspector.clazz(firstBackportFromDesugarVarHandle.getHolderClass()),
+ usesNativeVarHandle ? not(isPresent()) : isPresent());
+ // Currently DesugarMethodHandlesLookup this is fully inlined by R8.
+ assertThat(
+ inspector.clazz(
+ "j$."
+ + DescriptorUtils.descriptorToJavaType(
+ DexItemFactory.desugarMethodHandlesLookupDescriptorString)),
+ usesNativeVarHandle || compilationSpecification.isL8Shrink()
+ ? not(isPresent())
+ : isPresent());
}
void runTest(List<String> toRun) throws Exception {
+ // Skip test with minimal configuration before API level 24, as the test use stream.
+ assumeTrue(
+ libraryDesugaringSpecification != JDK11_MINIMAL
+ || parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N));
+
String verbosity = "2";
DesugaredLibraryTestCompileResult<?> compileResult =
testForDesugaredLibrary(
@@ -125,30 +184,14 @@
// internal state of the implementation, so desugaring is needed for the program here.
.addOptionsModification(options -> options.enableVarHandleDesugaring = true)
.compile()
+ .inspectL8(this::inspect)
.withArt6Plus64BitsLib();
for (String success : toRun) {
SingleTestRunResult<?> result =
compileResult.run(parameters.getRuntime(), "TestNGMainRunner", verbosity, success);
- if ((parameters.asDexRuntime().getVersion().equals(Version.V5_1_1)
- || parameters.asDexRuntime().getVersion().equals(Version.V6_0_1))
- && libraryDesugaringSpecification == JDK11_MINIMAL) {
- // Some tests use streams, so which is not desugared with JDK11_MINIMAL. These tests are
- // somehow skipped by the test runner used in the JDK11 tests.
- assertTrue(result.getStdOut().contains("Total tests run: 9, Failures: 0, Skips: 7"));
- assertTrue(result.getStdOut().contains(StringUtils.lines(success + ": SUCCESS")));
- } else if (parameters.asDexRuntime().getVersion().isOlderThanOrEqual(Version.V12_0_0)) {
- ranWithSuccessOrFailures(success, result);
- } else {
- assertTrue(parameters.asDexRuntime().getVersion().isNewerThanOrEqual(Version.V13_0_0));
- if (parameters.getApiLevel() == AndroidApiLevel.B) {
- ranWithSuccessOrFailures(success, result);
- } else {
- // No desugaring and JDK11 based runtime implementation.
- assertTrue(
- "Failure in " + success + "\n" + result,
- result.getStdOut().contains(StringUtils.lines(success + ": SUCCESS")));
- }
- }
+ assertTrue(
+ "Failure in " + success + "\n" + result,
+ result.getStdOut().contains(StringUtils.lines(success + ": SUCCESS")));
}
}