Reapply "Android U supports records and sealed classes"
This reverts commit b2c65396524b6f1c579458f1c1c549cc8fa0648c.
Original commit message:
Android U supports records and sealed classes
For API level is 34 and above generate code for native support of
records and sealed classes.
Fixes: b/293592205
Fixes: b/293987311
Bug: b/293591931
Change-Id: I62680cf074368a0d906446a2abbf8321da172d65
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5f11e97..79623e1 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -64,7 +64,7 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.PrefixRewritingNamingLens;
import com.android.tools.r8.naming.ProguardMapMinifier;
-import com.android.tools.r8.naming.RecordInvokeDynamicRewriter;
+import com.android.tools.r8.naming.RecordInvokeDynamicInvokeCustomRewriter;
import com.android.tools.r8.naming.RecordRewritingNamingLens;
import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
import com.android.tools.r8.optimize.LegacyAccessModifier;
@@ -774,7 +774,8 @@
new IdentifierMinifier(appView, NamingLens.getIdentityLens()).run(executorService);
timing.end();
timing.begin("RecordInvokeDynamicRewrite");
- new RecordInvokeDynamicRewriter(appView, NamingLens.getIdentityLens()).run(executorService);
+ new RecordInvokeDynamicInvokeCustomRewriter(appView, NamingLens.getIdentityLens())
+ .run(executorService);
timing.end();
}
appView.appInfo().notifyMinifierFinished();
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 3d05de8..78beee1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -583,38 +583,25 @@
}
}
- if (appView.options().emitRecordAnnotationsExInDex) {
- return new DexAnnotation(
- VISIBILITY_SYSTEM,
- new DexEncodedAnnotation(
- factory.annotationRecord,
- new DexAnnotationElement[] {
- new DexAnnotationElement(
- factory.annotationRecordComponentNames, new DexValueArray(componentNames)),
- new DexAnnotationElement(
- factory.annotationRecordComponentTypes, new DexValueArray(componentTypes)),
- new DexAnnotationElement(
- factory.annotationRecordComponentSignatures,
- new DexValueArray(componentSignatures)),
- new DexAnnotationElement(
- factory.annotationRecordComponentAnnotationVisibilities,
- new DexValueArray(componentAnnotationVisibilities)),
- new DexAnnotationElement(
- factory.annotationRecordComponentAnnotations,
- new DexValueArray(componentAnnotations))
- }));
- } else {
- return new DexAnnotation(
- VISIBILITY_SYSTEM,
- new DexEncodedAnnotation(
- factory.annotationRecord,
- new DexAnnotationElement[] {
- new DexAnnotationElement(
- factory.annotationRecordComponentNames, new DexValueArray(componentNames)),
- new DexAnnotationElement(
- factory.annotationRecordComponentTypes, new DexValueArray(componentTypes))
- }));
- }
+ return new DexAnnotation(
+ VISIBILITY_SYSTEM,
+ new DexEncodedAnnotation(
+ factory.annotationRecord,
+ new DexAnnotationElement[] {
+ new DexAnnotationElement(
+ factory.annotationRecordComponentNames, new DexValueArray(componentNames)),
+ new DexAnnotationElement(
+ factory.annotationRecordComponentTypes, new DexValueArray(componentTypes)),
+ new DexAnnotationElement(
+ factory.annotationRecordComponentSignatures,
+ new DexValueArray(componentSignatures)),
+ new DexAnnotationElement(
+ factory.annotationRecordComponentAnnotationVisibilities,
+ new DexValueArray(componentAnnotationVisibilities)),
+ new DexAnnotationElement(
+ factory.annotationRecordComponentAnnotations,
+ new DexValueArray(componentAnnotations))
+ }));
}
public static String getSignature(DexAnnotation signatureAnnotation) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 1e3e943..8d63fb1 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -40,6 +40,7 @@
import com.android.tools.r8.horizontalclassmerging.policies.NoKeepRules;
import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinMetadata;
import com.android.tools.r8.horizontalclassmerging.policies.NoNativeMethods;
+import com.android.tools.r8.horizontalclassmerging.policies.NoRecords;
import com.android.tools.r8.horizontalclassmerging.policies.NoResourceClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoServiceLoaders;
import com.android.tools.r8.horizontalclassmerging.policies.NoVerticallyMergedClasses;
@@ -174,7 +175,8 @@
new NoInstanceFieldAnnotations(),
new NoKotlinMetadata(),
new NoNativeMethods(),
- new NoServiceLoaders(appView));
+ new NoServiceLoaders(appView),
+ new NoRecords());
}
private static boolean verifySingleClassPoliciesIrrelevantForMergingSynthetics(
@@ -192,7 +194,8 @@
new NoInstanceFieldAnnotations(),
new NoKotlinMetadata(),
new NoNativeMethods(),
- new NoServiceLoaders(appView));
+ new NoServiceLoaders(appView),
+ new NoRecords());
policies.stream().map(VerifySingleClassPolicyAlwaysSatisfied::new).forEach(builder::add);
return true;
}
@@ -208,7 +211,8 @@
new NoInnerClasses(),
new NoInstanceFieldAnnotations(),
new NoKotlinMetadata(),
- new NoNativeMethods());
+ new NoNativeMethods(),
+ new NoRecords());
policies.stream().map(VerifySingleClassPolicyAlwaysSatisfied::new).forEach(builder::add);
return true;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRecords.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRecords.java
new file mode 100644
index 0000000..d516fbb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRecords.java
@@ -0,0 +1,22 @@
+// 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.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+
+public class NoRecords extends SingleClassPolicy {
+
+ @Override
+ public boolean canMerge(DexProgramClass program) {
+ // TODO(b/299094325): Allow merging record classes.
+ return !program.isRecord();
+ }
+
+ @Override
+ public String getName() {
+ return "NoRecords";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 0ee142f..710fb47 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -221,4 +221,11 @@
void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
registry.registerCallSite(callSite);
}
+
+ @Override
+ protected boolean needsRangedInvoke(DexBuilder builder) {
+ return builder.getOptions().testing.forceInvokeRangeForInvokeCustom
+ ? true
+ : super.needsRangedInvoke(builder);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfToCfRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfToCfRewriter.java
deleted file mode 100644
index 14c854b..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfToCfRewriter.java
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar.records;
-
-import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeDynamicOnRecord;
-import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.parseInvokeDynamicOnRecord;
-
-import com.android.tools.r8.cf.code.CfInvokeDynamic;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
-import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.graph.DexValue.DexValueType;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.lens.GraphLens;
-import com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.RecordInvokeDynamic;
-import com.android.tools.r8.naming.NamingLens;
-import java.util.ArrayList;
-
-/** Used to shrink records in Cf to Cf compilations */
-public class RecordCfToCfRewriter {
-
- private final AppView<?> appView;
-
- public static RecordCfToCfRewriter create(AppView<?> appView) {
- if (appView.enableWholeProgramOptimizations() && appView.options().isGeneratingClassFiles()) {
- return new RecordCfToCfRewriter(appView);
- }
- return null;
- }
-
- private RecordCfToCfRewriter(AppView<?> appView) {
- this.appView = appView;
- }
-
- // Called after final tree shaking, prune and minify field names and field values.
- public CfInvokeDynamic rewriteRecordInvokeDynamic(
- CfInvokeDynamic invokeDynamic, ProgramMethod context, NamingLens namingLens) {
- if (!isInvokeDynamicOnRecord(invokeDynamic, appView, context)) {
- return invokeDynamic;
- }
- RecordInvokeDynamic recordInvokeDynamic =
- parseInvokeDynamicOnRecord(invokeDynamic, appView, context);
- DexString newFieldNames =
- recordInvokeDynamic
- .computeRecordFieldNamesComputationInfo()
- .internalComputeNameFor(
- recordInvokeDynamic.getRecordType(), appView, appView.graphLens(), namingLens);
- DexField[] newFields = computePresentFields(appView.graphLens(), recordInvokeDynamic);
- return writeRecordInvokeDynamic(
- recordInvokeDynamic.withFieldNamesAndFields(newFieldNames, newFields));
- }
-
- private DexField[] computePresentFields(
- GraphLens graphLens, RecordInvokeDynamic recordInvokeDynamic) {
- ArrayList<DexField> finalFields = new ArrayList<>();
- for (DexField field : recordInvokeDynamic.getFields()) {
- DexEncodedField dexEncodedField =
- recordInvokeDynamic
- .getRecordClass()
- .lookupInstanceField(graphLens.getRenamedFieldSignature(field));
- if (dexEncodedField != null) {
- finalFields.add(field);
- }
- }
- DexField[] newFields = new DexField[finalFields.size()];
- for (int i = 0; i < finalFields.size(); i++) {
- newFields[i] = finalFields.get(i);
- }
- return newFields;
- }
-
- private CfInvokeDynamic writeRecordInvokeDynamic(RecordInvokeDynamic recordInvokeDynamic) {
- DexItemFactory factory = appView.dexItemFactory();
- DexMethodHandle bootstrapMethod =
- new DexMethodHandle(
- MethodHandleType.INVOKE_STATIC, factory.objectMethodsMembers.bootstrap, false, null);
- ArrayList<DexValue> bootstrapArgs = new ArrayList<>();
- bootstrapArgs.add(new DexValueType(recordInvokeDynamic.getRecordType()));
- bootstrapArgs.add(new DexValueString(recordInvokeDynamic.getFieldNames()));
- for (DexField field : recordInvokeDynamic.getFields()) {
- bootstrapArgs.add(
- new DexValueMethodHandle(
- new DexMethodHandle(MethodHandleType.INSTANCE_GET, field, false, null)));
- }
- return new CfInvokeDynamic(
- factory.createCallSite(
- recordInvokeDynamic.getMethodName(),
- recordInvokeDynamic.getMethodProto(),
- bootstrapMethod,
- bootstrapArgs));
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
new file mode 100644
index 0000000..7cd44a8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
@@ -0,0 +1,175 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.records;
+
+import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeCustomOnRecord;
+import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeDynamicOnRecord;
+import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.parseInvokeCustomOnRecord;
+import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.parseInvokeDynamicOnRecord;
+
+import com.android.tools.r8.cf.code.CfInvokeDynamic;
+import com.android.tools.r8.dex.code.DexInstruction;
+import com.android.tools.r8.dex.code.DexInvokeCustom;
+import com.android.tools.r8.dex.code.DexInvokeCustomRange;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.lens.GraphLens;
+import com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.RecordInvokeDynamic;
+import com.android.tools.r8.naming.NamingLens;
+import java.util.ArrayList;
+
+/** Used to rewrite invokedynamic/invoke-custom when shrinking and minifying records. */
+public class RecordRewriter {
+
+ private final AppView<?> appView;
+
+ public static RecordRewriter create(AppView<?> appView) {
+ if (appView.enableWholeProgramOptimizations()) {
+ return new RecordRewriter(appView);
+ }
+ return null;
+ }
+
+ private RecordRewriter(AppView<?> appView) {
+ this.appView = appView;
+ }
+
+ // Called after final tree shaking, prune and minify field names and field values.
+ public CfInvokeDynamic rewriteRecordInvokeDynamic(
+ CfInvokeDynamic invokeDynamic, ProgramMethod context, NamingLens namingLens) {
+ if (!isInvokeDynamicOnRecord(invokeDynamic, appView, context)) {
+ return invokeDynamic;
+ }
+ RecordInvokeDynamic recordInvokeDynamic =
+ parseInvokeDynamicOnRecord(invokeDynamic, appView, context);
+ DexString newFieldNames =
+ recordInvokeDynamic
+ .computeRecordFieldNamesComputationInfo()
+ .internalComputeNameFor(
+ recordInvokeDynamic.getRecordType(), appView, appView.graphLens(), namingLens);
+ DexField[] newFields = computePresentFields(appView.graphLens(), recordInvokeDynamic);
+ return writeRecordInvokeDynamic(
+ recordInvokeDynamic.withFieldNamesAndFields(newFieldNames, newFields));
+ }
+
+ public DexInstruction rewriteRecordInvokeCustom(
+ DexInstruction invokeCustom, ProgramMethod context, NamingLens namingLens) {
+ if (!isInvokeCustomOnRecord(invokeCustom, appView, context)) {
+ return invokeCustom;
+ }
+ RecordInvokeDynamic recordInvokeDynamic =
+ parseInvokeCustomOnRecord(invokeCustom, appView, context);
+ DexString newFieldNames =
+ recordInvokeDynamic
+ .computeRecordFieldNamesComputationInfo()
+ .internalComputeNameFor(
+ recordInvokeDynamic.getRecordType(), appView, appView.graphLens(), namingLens);
+ DexField[] newFields = computePresentFields(appView.graphLens(), recordInvokeDynamic);
+ return writeRecordInvokeCustom(
+ invokeCustom, recordInvokeDynamic.withFieldNamesAndFields(newFieldNames, newFields));
+ }
+
+ private DexField[] computePresentFields(
+ GraphLens graphLens, RecordInvokeDynamic recordInvokeDynamic) {
+ ArrayList<DexField> finalFields = new ArrayList<>();
+ for (DexField field : recordInvokeDynamic.getFields()) {
+ DexEncodedField dexEncodedField =
+ recordInvokeDynamic
+ .getRecordClass()
+ .lookupInstanceField(graphLens.getRenamedFieldSignature(field));
+ if (dexEncodedField != null) {
+ finalFields.add(field);
+ }
+ }
+ DexField[] newFields = new DexField[finalFields.size()];
+ for (int i = 0; i < finalFields.size(); i++) {
+ newFields[i] = finalFields.get(i);
+ }
+ return newFields;
+ }
+
+ @SuppressWarnings("ReferenceEquality")
+ private CfInvokeDynamic writeRecordInvokeDynamic(RecordInvokeDynamic recordInvokeDynamic) {
+ DexItemFactory factory = appView.dexItemFactory();
+ DexMethodHandle bootstrapMethod =
+ factory.createMethodHandle(
+ MethodHandleType.INVOKE_STATIC, factory.objectMethodsMembers.bootstrap, false, null);
+ ArrayList<DexValue> bootstrapArgs = new ArrayList<>();
+ bootstrapArgs.add(new DexValueType(recordInvokeDynamic.getRecordType()));
+ bootstrapArgs.add(new DexValueString(recordInvokeDynamic.getFieldNames()));
+ for (DexField field : recordInvokeDynamic.getFields()) {
+ assert recordInvokeDynamic.getRecordCodeType() == field.getHolderType();
+ bootstrapArgs.add(
+ new DexValueMethodHandle(
+ new DexMethodHandle(MethodHandleType.INSTANCE_GET, field, false, null)));
+ }
+ return new CfInvokeDynamic(
+ factory.createCallSite(
+ recordInvokeDynamic.getMethodName(),
+ recordInvokeDynamic.getMethodProto(),
+ bootstrapMethod,
+ bootstrapArgs));
+ }
+
+ private DexInstruction writeRecordInvokeCustom(
+ DexInstruction invokeCustom, RecordInvokeDynamic recordInvokeDynamic) {
+ DexItemFactory factory = appView.dexItemFactory();
+ DexMethodHandle bootstrapMethod =
+ factory.createMethodHandle(
+ MethodHandleType.INVOKE_STATIC, factory.objectMethodsMembers.bootstrap, false, null);
+ ArrayList<DexValue> bootstrapArgs = new ArrayList<>();
+ bootstrapArgs.add(new DexValueType(recordInvokeDynamic.getRecordCodeType()));
+ bootstrapArgs.add(new DexValueString(recordInvokeDynamic.getFieldNames()));
+ for (DexField field : recordInvokeDynamic.getFields()) {
+ // Rewrite using the code type of the field.
+ DexField codeField =
+ factory.createField(recordInvokeDynamic.getRecordCodeType(), field.type, field.name);
+ bootstrapArgs.add(
+ new DexValueMethodHandle(
+ factory.createMethodHandle(MethodHandleType.INSTANCE_GET, codeField, false, null)));
+ }
+ if (invokeCustom instanceof DexInvokeCustom) {
+ DexInvokeCustom current = (DexInvokeCustom) invokeCustom;
+ DexInvokeCustom rewritten =
+ new DexInvokeCustom(
+ current.A,
+ factory.createCallSite(
+ recordInvokeDynamic.getMethodName(),
+ recordInvokeDynamic.getMethodProto(),
+ bootstrapMethod,
+ bootstrapArgs),
+ current.C,
+ current.D,
+ current.E,
+ current.F,
+ current.G);
+ rewritten.setOffset(current.getOffset());
+ return rewritten;
+ } else {
+ DexInvokeCustomRange current = (DexInvokeCustomRange) invokeCustom;
+ DexInvokeCustomRange rewritten =
+ new DexInvokeCustomRange(
+ current.CCCC,
+ current.AA,
+ factory.createCallSite(
+ recordInvokeDynamic.getMethodName(),
+ recordInvokeDynamic.getMethodProto(),
+ bootstrapMethod,
+ bootstrapArgs));
+ rewritten.setOffset(current.getOffset());
+ return rewritten;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriterHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriterHelper.java
index 50c6963..f465a98 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriterHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriterHelper.java
@@ -5,6 +5,9 @@
package com.android.tools.r8.ir.desugar.records;
import com.android.tools.r8.cf.code.CfInvokeDynamic;
+import com.android.tools.r8.dex.code.DexInstruction;
+import com.android.tools.r8.dex.code.DexInvokeCustom;
+import com.android.tools.r8.dex.code.DexInvokeCustomRange;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
@@ -28,6 +31,12 @@
return isInvokeDynamicOnRecord(invokeDynamic.getCallSite(), appView, context);
}
+ public static boolean isInvokeCustomOnRecord(
+ DexInstruction invokeCustom, AppView<?> appView, ProgramMethod context) {
+ assert invokeCustom instanceof DexInvokeCustom || invokeCustom instanceof DexInvokeCustomRange;
+ return isInvokeDynamicOnRecord(invokeCustom.getCallSite(), appView, context);
+ }
+
@SuppressWarnings("ReferenceEquality")
public static boolean isInvokeDynamicOnRecord(
DexCallSite callSite, AppView<?> appView, ProgramMethod context) {
@@ -61,7 +70,10 @@
assert false : "Invoke-dynamic invoking method ObjectMethods#bootstrap with an invalid type.";
return false;
}
- DexClass recordClass = appView.definitionFor(recordType.getValue(), context);
+ DexType codeRecordType = recordType.getValue();
+ DexClass recordClass =
+ appView.definitionFor(
+ appView.graphLens().lookupType(codeRecordType, appView.codeLens()), context);
if (recordClass == null || recordClass.isNotProgramClass()) {
return false;
}
@@ -87,16 +99,16 @@
}
// 3. Check it matches one of the 3 invokeDynamicOnRecord instruction.
if (callSite.methodName == factory.toStringMethodName) {
- assert callSite.methodProto == factory.createProto(factory.stringType, recordClass.getType());
+ assert callSite.methodProto == factory.createProto(factory.stringType, codeRecordType);
return true;
}
if (callSite.methodName == factory.hashCodeMethodName) {
- assert callSite.methodProto == factory.createProto(factory.intType, recordClass.getType());
+ assert callSite.methodProto == factory.createProto(factory.intType, codeRecordType);
return true;
}
if (callSite.methodName == factory.equalsMethodName) {
assert callSite.methodProto
- == factory.createProto(factory.booleanType, recordClass.getType(), factory.objectType);
+ == factory.createProto(factory.booleanType, codeRecordType, factory.objectType);
return true;
}
return false;
@@ -105,7 +117,17 @@
public static RecordInvokeDynamic parseInvokeDynamicOnRecord(
CfInvokeDynamic invokeDynamic, AppView<?> appView, ProgramMethod context) {
assert isInvokeDynamicOnRecord(invokeDynamic, appView, context);
- DexCallSite callSite = invokeDynamic.getCallSite();
+ return parseInvokeDynamicOnRecord(invokeDynamic.getCallSite(), appView, context);
+ }
+
+ public static RecordInvokeDynamic parseInvokeCustomOnRecord(
+ DexInstruction invokeCustom, AppView<?> appView, ProgramMethod context) {
+ assert isInvokeCustomOnRecord(invokeCustom, appView, context);
+ return parseInvokeDynamicOnRecord(invokeCustom.getCallSite(), appView, context);
+ }
+
+ public static RecordInvokeDynamic parseInvokeDynamicOnRecord(
+ DexCallSite callSite, AppView<?> appView, ProgramMethod context) {
DexValueType recordValueType = callSite.bootstrapArgs.get(0).asDexValueType();
DexValueString valueString = callSite.bootstrapArgs.get(1).asDexValueString();
DexString fieldNames = valueString.getValue();
@@ -114,10 +136,11 @@
DexValueMethodHandle handle = callSite.bootstrapArgs.get(i).asDexValueMethodHandle();
fields[i - 2] = handle.value.member.asDexField();
}
+ DexType recordCodeType = recordValueType.getValue();
DexProgramClass recordClass =
- appView.definitionFor(recordValueType.getValue()).asProgramClass();
+ appView.definitionFor(appView.graphLens().lookupType(recordCodeType)).asProgramClass();
return new RecordInvokeDynamic(
- callSite.methodName, callSite.methodProto, fieldNames, fields, recordClass);
+ callSite.methodName, callSite.methodProto, fieldNames, fields, recordClass, recordCodeType);
}
static class RecordInvokeDynamic {
@@ -127,22 +150,26 @@
private final DexString fieldNames;
private final DexField[] fields;
private final DexProgramClass recordClass;
+ private final DexType recordCodeType;
private RecordInvokeDynamic(
DexString methodName,
DexProto methodProto,
DexString fieldNames,
DexField[] fields,
- DexProgramClass recordClass) {
+ DexProgramClass recordClass,
+ DexType recordCodeType) {
this.methodName = methodName;
this.methodProto = methodProto;
this.fieldNames = fieldNames;
this.fields = fields;
this.recordClass = recordClass;
+ this.recordCodeType = recordCodeType;
}
RecordInvokeDynamic withFieldNamesAndFields(DexString fieldNames, DexField[] fields) {
- return new RecordInvokeDynamic(methodName, methodProto, fieldNames, fields, recordClass);
+ return new RecordInvokeDynamic(
+ methodName, methodProto, fieldNames, fields, recordClass, recordCodeType);
}
DexField[] getFields() {
@@ -153,6 +180,10 @@
return recordClass.getType();
}
+ DexType getRecordCodeType() {
+ return recordCodeType;
+ }
+
DexProgramClass getRecordClass() {
return recordClass;
}
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 40490d4..74c76d1 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -87,7 +87,7 @@
timing.end();
timing.begin("RecordInvokeDynamicRewrite");
- new RecordInvokeDynamicRewriter(appView, lens).run(executorService);
+ new RecordInvokeDynamicInvokeCustomRewriter(appView, lens).run(executorService);
timing.end();
appView.notifyOptimizationFinishedForTesting();
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 423978d..24fb83a 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -157,7 +157,7 @@
timing.end();
timing.begin("RecordInvokeDynamicRewrite");
- new RecordInvokeDynamicRewriter(appView, lens).run(executorService);
+ new RecordInvokeDynamicInvokeCustomRewriter(appView, lens).run(executorService);
timing.begin("MinifyIdentifiers");
appView.notifyOptimizationFinishedForTesting();
diff --git a/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicInvokeCustomRewriter.java b/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicInvokeCustomRewriter.java
new file mode 100644
index 0000000..78eb905
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicInvokeCustomRewriter.java
@@ -0,0 +1,86 @@
+// 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.naming;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.dex.code.DexInstruction;
+import com.android.tools.r8.dex.code.DexInvokeCustom;
+import com.android.tools.r8.dex.code.DexInvokeCustomRange;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.records.RecordRewriter;
+import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+/** Rewrites the record invokedynamic/invoke-custom in hashCode, equals and toString. */
+public class RecordInvokeDynamicInvokeCustomRewriter {
+
+ private final AppView<?> appView;
+ private final RecordRewriter recordRewriter;
+ private final NamingLens lens;
+
+ public RecordInvokeDynamicInvokeCustomRewriter(AppView<?> appView, NamingLens lens) {
+ this.appView = appView;
+ this.recordRewriter = RecordRewriter.create(appView);
+ this.lens = lens;
+ }
+
+ public void run(ExecutorService executorService) throws ExecutionException {
+ ThreadUtils.processItems(
+ appView.appInfo().classes(),
+ clazz -> {
+ clazz.forEachProgramMethodMatching(
+ DexEncodedMethod::hasCode, this::rewriteRecordInvokeDynamicInMethod);
+ },
+ executorService);
+ }
+
+ private void rewriteRecordInvokeDynamicInMethod(ProgramMethod programMethod) {
+ if (recordRewriter == null) {
+ return;
+ }
+ if (!programMethod.getHolder().isRecord()) {
+ return;
+ }
+ Code code = programMethod.getDefinition().getCode();
+ assert code != null;
+ if (code.isDexCode()) {
+ DexInstruction[] instructions = code.asDexCode().instructions;
+ DexInstruction[] newInstructions =
+ ArrayUtils.map(
+ instructions,
+ (DexInstruction instruction) -> {
+ if (instruction instanceof DexInvokeCustom
+ || instruction instanceof DexInvokeCustomRange) {
+ return recordRewriter.rewriteRecordInvokeCustom(instruction, programMethod, lens);
+ }
+ return instruction;
+ },
+ DexInstruction.EMPTY_ARRAY);
+ if (newInstructions != instructions) {
+ programMethod.setCode(code.asDexCode().withNewInstructions(newInstructions), appView);
+ }
+ } else if (code.isCfCode()) {
+ List<CfInstruction> instructions = code.asCfCode().getInstructions();
+ List<CfInstruction> newInstructions =
+ ListUtils.mapOrElse(
+ instructions,
+ (int index, CfInstruction instruction) -> {
+ if (instruction.isInvokeDynamic()) {
+ return recordRewriter.rewriteRecordInvokeDynamic(
+ instruction.asInvokeDynamic(), programMethod, lens);
+ }
+ return instruction;
+ },
+ instructions);
+ code.asCfCode().setInstructions(newInstructions);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicRewriter.java b/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicRewriter.java
deleted file mode 100644
index 80b2e33..0000000
--- a/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicRewriter.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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.naming;
-
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.records.RecordCfToCfRewriter;
-import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.ThreadUtils;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-
-/** Rewrites the record invokedynamic in hashCode, equals and toString. */
-public class RecordInvokeDynamicRewriter {
-
- private final AppView<?> appView;
- private final RecordCfToCfRewriter recordCfToCfRewriter;
- private final NamingLens lens;
-
- public RecordInvokeDynamicRewriter(AppView<?> appView, NamingLens lens) {
- this.appView = appView;
- this.recordCfToCfRewriter = RecordCfToCfRewriter.create(appView);
- this.lens = lens;
- }
-
- public void run(ExecutorService executorService) throws ExecutionException {
- ThreadUtils.processItems(
- appView.appInfo().classes(),
- clazz -> {
- clazz.forEachProgramMethodMatching(
- DexEncodedMethod::hasCode, this::rewriteRecordInvokeDynamicInMethod);
- },
- executorService);
- }
-
- private void rewriteRecordInvokeDynamicInMethod(ProgramMethod programMethod) {
- if (recordCfToCfRewriter == null) {
- return;
- }
- if (!programMethod.getHolder().isRecord()) {
- return;
- }
- Code code = programMethod.getDefinition().getCode();
- assert code != null;
- if (!code.isCfCode()) {
- return;
- }
- List<CfInstruction> instructions = code.asCfCode().getInstructions();
- List<CfInstruction> newInstructions =
- ListUtils.mapOrElse(
- instructions,
- (int index, CfInstruction instruction) -> {
- if (instruction.isInvokeDynamic()) {
- return recordCfToCfRewriter.rewriteRecordInvokeDynamic(
- instruction.asInvokeDynamic(), programMethod, lens);
- }
- return instruction;
- },
- instructions);
- code.asCfCode().setInstructions(newInstructions);
- }
-}
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 6c81818..de7a1b5 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -468,18 +468,18 @@
public boolean createSingletonsForStatelessLambdas =
System.getProperty("com.android.tools.r8.createSingletonsForStatelessLambdas") != null;
- // Flag to allow record annotations in DEX. See b/231930852 for context.
- public boolean emitRecordAnnotationsInDex =
+ // TODO(b/293591931): Remove this flag.
+ // Flag to allow record annotations in DEX. See b/231930852 for context.
+ private final boolean emitRecordAnnotationsInDex =
System.getProperty("com.android.tools.r8.emitRecordAnnotationsInDex") != null;
- public boolean emitRecordAnnotationsExInDex =
- System.getProperty("com.android.tools.r8.emitRecordAnnotationsExInDex") != null;
// Flag to allow nest annotations in DEX. See b/231930852 for context.
public boolean emitNestAnnotationsInDex =
System.getProperty("com.android.tools.r8.emitNestAnnotationsInDex") != null;
+ // TODO(b/293591931): Remove this flag.
// Flag to allow permitted subclasses annotations in DEX. See b/231930852 for context.
- public boolean emitPermittedSubclassesAnnotationsInDex =
+ private final boolean emitPermittedSubclassesAnnotationsInDex =
System.getProperty("com.android.tools.r8.emitPermittedSubclassesAnnotationsInDex") != null;
private DumpInputFlags dumpInputFlags = DumpInputFlags.getDefault();
@@ -2249,6 +2249,8 @@
public boolean enableBinopOptimization = true;
+ public boolean forceInvokeRangeForInvokeCustom = false;
+
private DeterminismChecker getDeterminismChecker() {
// Lazily read the env-var so that it can be set after options init.
if (determinismChecker == null && !hasReadCheckDeterminism) {
@@ -2645,11 +2647,11 @@
}
public boolean canUseRecords() {
- return hasFeaturePresentFrom(null) || emitRecordAnnotationsInDex;
+ return hasFeaturePresentFrom(AndroidApiLevel.U) || emitRecordAnnotationsInDex;
}
public boolean canUseSealedClasses() {
- return hasFeaturePresentFrom(null) || emitPermittedSubclassesAnnotationsInDex;
+ return hasFeaturePresentFrom(AndroidApiLevel.U) || emitPermittedSubclassesAnnotationsInDex;
}
public boolean canLeaveStaticInterfaceMethodInvokes() {
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index a44a64e..5d74954 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -101,6 +101,17 @@
}
@SuppressWarnings("UnnecessaryParentheses")
+ public static boolean containsEntry(Path zipfile, String name) throws IOException {
+ BooleanBox result = new BooleanBox();
+ ZipUtils.iter(
+ zipfile,
+ (entry, stream) -> {
+ result.computeIfNotSet(() -> entry.getName().equals(name));
+ });
+ return result.get();
+ }
+
+ @SuppressWarnings("UnnecessaryParentheses")
public static Path map(
Path zipFilePath, Path mappedFilePath, BiFunction<ZipEntry, byte[], byte[]> map)
throws IOException {
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index d6032c7..6b4518d 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1701,6 +1701,11 @@
.isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport());
}
+ public static boolean runtimeWithRecordsSupport(TestRuntime runtime) {
+ return (runtime.isCf() && runtime.asCf().hasRecordsSupport())
+ || (runtime.isDex() && runtime.asDex().hasRecordsSupport());
+ }
+
public static AndroidApiLevel apiLevelWithStaticInterfaceMethodsSupport() {
return AndroidApiLevel.N;
}
@@ -1737,6 +1742,10 @@
return AndroidApiLevel.O;
}
+ public static boolean canUseNativeRecords(TestParameters parameters) {
+ return parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.U);
+ }
+
public static boolean canUseJavaUtilObjects(TestParameters parameters) {
return parameters.isCfRuntime()
|| parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K);
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 7ca196a..892c68f 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -101,17 +101,6 @@
return false;
}
- public boolean canUseRecords() {
- assert isCfRuntime() || isDexRuntime();
- return isCfRuntime() && asCfRuntime().isNewerThanOrEqual(CfVm.JDK14);
- }
-
- public boolean canUseRecordsWhenDesugaring() {
- assert isCfRuntime() || isDexRuntime();
- assert apiLevel != null;
- return false;
- }
-
public boolean canUseFilledNewArrayOnNonStringObjects() {
return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N);
}
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index 3669e77..77e4e22 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -84,6 +84,12 @@
public static CfVm getMinimumSystemVersion() {
return JDK11;
}
+
+ // Records was experimental from JDK-15 (requiring turning on experimental feaures), and GA
+ // in JDK-17.
+ public boolean hasRecordsSupport() {
+ return isGreaterThanOrEqualTo(JDK17);
+ }
}
private static final Path JDK8_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk8");
@@ -308,6 +314,10 @@
public AndroidApiLevel getMinApiLevel() {
return ToolHelper.getMinApiLevelForDexVm(vm);
}
+
+ public boolean hasRecordsSupport() {
+ return getVersion().hasRecordsSupport();
+ }
}
// Wrapper for the CF runtimes.
@@ -384,6 +394,10 @@
public boolean isNewerThanOrEqual(CfVm version) {
return vm == version || !vm.lessThanOrEqual(version);
}
+
+ public boolean hasRecordsSupport() {
+ return getVm().hasRecordsSupport();
+ }
}
public <T> T match(Function<CfRuntime, T> onCf, BiFunction<DexRuntime, DexVm.Version, T> onDex) {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index d93ed8c..21147f8 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -396,6 +396,10 @@
return isNewerThanOrEqual(start) && isOlderThanOrEqual(end);
}
+ public boolean hasRecordsSupport() {
+ return isNewerThanOrEqual(V14_0_0);
+ }
+
public String toString() {
return shortName;
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java
index 874a60c..c2846c5 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java
@@ -5,12 +5,10 @@
package com.android.tools.r8.desugar.records;
-import com.android.tools.r8.R8FullTestBuilder;
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.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -22,9 +20,9 @@
private static final String RECORD_NAME = "EmptyRecordAnnotation";
private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
- private static final String EXPECTED_RESULT_CF =
+ private static final String EXPECTED_RESULT_NATIVE_RECORD =
StringUtils.lines("class java.lang.Record", "class records.EmptyRecordAnnotation$Empty");
- private static final String EXPECTED_RESULT_DEX =
+ private static final String EXPECTED_RESULT_DESUGARED_RECORD =
StringUtils.lines(
"class com.android.tools.r8.RecordTag", "class records.EmptyRecordAnnotation$Empty");
@@ -43,50 +41,49 @@
.build();
}
- private boolean isDefaultCfParameters() {
- return parameters.isCfRuntime() && parameters.getApiLevel().equals(AndroidApiLevel.B);
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addProgramClassFileData(PROGRAM_DATA)
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT_NATIVE_RECORD);
}
@Test
- public void testD8AndJvm() throws Exception {
- if (isDefaultCfParameters()) {
- testForJvm(parameters)
- .addProgramClassFileData(PROGRAM_DATA)
- .run(parameters.getRuntime(), MAIN_TYPE)
- .assertSuccessWithOutput(EXPECTED_RESULT_CF);
- }
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
testForD8(parameters.getBackend())
.addProgramClassFileData(PROGRAM_DATA)
.setMinApi(parameters)
.compile()
.run(parameters.getRuntime(), MAIN_TYPE)
- .assertSuccessWithOutput(EXPECTED_RESULT_DEX);
+ .applyIf(
+ canUseNativeRecords(parameters),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT_NATIVE_RECORD),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT_DESUGARED_RECORD));
}
@Test
public void testR8() throws Exception {
parameters.assumeR8TestParameters();
- R8FullTestBuilder builder =
- testForR8(parameters.getBackend())
- .addProgramClassFileData(PROGRAM_DATA)
- .setMinApi(parameters)
- .addKeepRules("-keep class records.EmptyRecordAnnotation { *; }")
- .addKeepRules("-keepattributes *Annotation*")
- .addKeepRules("-keep class records.EmptyRecordAnnotation$Empty")
- .addKeepMainRule(MAIN_TYPE);
- if (parameters.isCfRuntime()) {
- builder
- .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
- .compile()
- .inspect(RecordTestUtils::assertRecordsAreRecords)
- .enableJVMPreview()
- .run(parameters.getRuntime(), MAIN_TYPE)
- .assertSuccessWithOutput(EXPECTED_RESULT_CF);
- return;
- }
- builder
- .addKeepRules("-keep class java.lang.Record")
+ boolean willDesugarRecords = parameters.isDexRuntime() && !canUseNativeRecords(parameters);
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
+ .addProgramClassFileData(PROGRAM_DATA)
+ .setMinApi(parameters)
+ .addKeepRules("-keep class records.EmptyRecordAnnotation { *; }")
+ .addKeepRules("-keepattributes *Annotation*")
+ .addKeepRules("-keep class records.EmptyRecordAnnotation$Empty")
+ .addKeepMainRule(MAIN_TYPE)
+ // This is used to avoid renaming com.android.tools.r8.RecordTag.
+ .applyIf(willDesugarRecords, b -> b.addKeepRules("-keep class java.lang.Record"))
+ .compile()
+ .applyIf(parameters.isCfRuntime(), r -> r.inspect(RecordTestUtils::assertRecordsAreRecords))
.run(parameters.getRuntime(), MAIN_TYPE)
- .assertSuccessWithOutput(EXPECTED_RESULT_DEX);
+ .applyIf(
+ !willDesugarRecords,
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT_NATIVE_RECORD),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT_DESUGARED_RECORD));
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java
index bcc90c5..0fcb6da 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableMap;
-import java.nio.file.Path;
import java.util.IdentityHashMap;
import java.util.Map;
import org.junit.Test;
@@ -42,12 +41,12 @@
+ CLASS
+ " { <fields>; }",
"a[name=%s]")
+ .put("-keep class " + CLASS + " { <fields>; }", "RecordBlog$Person[name=%s, age=42]")
.put(
"-keepclassmembers,allowobfuscation,allowoptimization class "
+ CLASS
+ " { <fields>; }",
"a[a=%s, b=42]")
- .put("-keep class " + CLASS + " { <fields>; }", "RecordBlog$Person[name=%s, age=42]")
.build();
@Parameter(0)
@@ -83,14 +82,16 @@
.addProgramClassFileData(PROGRAM_DATA)
.setMinApi(parameters)
.run(parameters.getRuntime(), MAIN_TYPE)
- .assertSuccessWithOutput(computeOutput(REFERENCE_OUTPUT_FORMAT));
+ .applyIf(
+ canUseNativeRecords(parameters) && !runtimeWithRecordsSupport(parameters.getRuntime()),
+ r -> r.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
+ r -> r.assertSuccessWithOutput(computeOutput(REFERENCE_OUTPUT_FORMAT)));
}
@Test
public void testR8() throws Exception {
parameters.assumeR8TestParameters();
assumeTrue(parameters.isDexRuntime() || isCfRuntimeWithNativeRecordSupport());
- Path[] jdk15LibraryFiles = RecordTestUtils.getJdk15LibraryFiles(temp);
Map<String, String> results = new IdentityHashMap<>();
KEEP_RULE_TO_OUTPUT_FORMAT.forEach(
(kr, outputFormat) -> {
@@ -105,7 +106,7 @@
if (parameters.isCfRuntime()) {
res =
builder
- .addLibraryFiles(jdk15LibraryFiles)
+ .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
.run(parameters.getRuntime(), MAIN_TYPE)
.getStdOut();
} else {
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentAnnotationsTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordComponentAnnotationsTest.java
index 14485c9..8ded32a 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordComponentAnnotationsTest.java
@@ -231,16 +231,7 @@
parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.U);
boolean runtimeWithNativeRecordSupport =
parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V14_0_0);
- testForDesugaring(
- parameters,
- options -> {
- if (compilingForNativeRecordSupport) {
- // TODO(b/231930852): When Art 14 support records this will be controlled by API
- // level.
- options.emitRecordAnnotationsInDex = true;
- options.emitRecordAnnotationsExInDex = true;
- }
- })
+ testForDesugaring(parameters)
.addProgramClassFileData(PROGRAM_DATA)
.run(parameters.getRuntime(), MAIN_TYPE)
.applyIf(
@@ -340,12 +331,6 @@
"records.RecordWithAnnotations$AnnotationRecordComponentOnly")
.applyIf(keepAnnotations, TestShrinkerBuilder::addKeepRuntimeVisibleAnnotations)
.setMinApi(parameters)
- .applyIf(
- compilingForNativeRecordSupport,
- // TODO(b/231930852): When Art 14 support records this will be controlled by API level.
- b ->
- b.addOptionsModification(options -> options.emitRecordAnnotationsInDex = true)
- .addOptionsModification(options -> options.emitRecordAnnotationsExInDex = true))
.compile()
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentSignatureTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordComponentSignatureTest.java
index db46699..ff6732a 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordComponentSignatureTest.java
@@ -98,16 +98,7 @@
boolean runningWithNativeRecordSupport =
parameters.getRuntime().isDex()
&& parameters.getRuntime().asDex().getVersion().isNewerThanOrEqual(Version.V14_0_0);
- testForDesugaring(
- parameters,
- options -> {
- if (compilingForNativeRecordSupport) {
- // TODO(b/231930852): When Art 14 support records this will be controlled by API
- // level.
- options.emitRecordAnnotationsInDex = true;
- options.emitRecordAnnotationsExInDex = true;
- }
- })
+ testForDesugaring(parameters)
.addProgramClassFileData(PROGRAM_DATA)
.run(parameters.getRuntime(), MAIN_TYPE)
.applyIf(
@@ -175,12 +166,6 @@
.addKeepMainRule(MAIN_TYPE)
.applyIf(keepSignatures, TestShrinkerBuilder::addKeepAttributeSignature)
.setMinApi(parameters)
- .applyIf(
- compilingForNativeRecordSupport,
- // TODO(b/231930852): When Art 14 support records this will be controlled by API level.
- b ->
- b.addOptionsModification(options -> options.emitRecordAnnotationsInDex = true)
- .addOptionsModification(options -> options.emitRecordAnnotationsExInDex = true))
.compile()
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java
index 2136f70..0c63278 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.desugar.records;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.GlobalSyntheticsConsumer;
@@ -64,7 +65,10 @@
.addProgramClassFileData(PROGRAM_DATA)
.setMinApi(parameters)
.run(parameters.getRuntime(), MAIN_TYPE)
- .assertSuccessWithOutput(EXPECTED_RESULT);
+ .applyIf(
+ canUseNativeRecords(parameters) && !runtimeWithRecordsSupport(parameters.getRuntime()),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT));
}
@Test
@@ -75,7 +79,9 @@
Path path = compileIntermediate(globals);
testForD8()
.addProgramFiles(path)
- .apply(
+ .applyIf(
+ canUseNativeRecords(parameters),
+ b -> assertFalse(globals.hasGlobals()),
b ->
b.getBuilder()
.addGlobalSyntheticsResourceProviders(globals.getIndexedModeProvider()))
@@ -97,7 +103,9 @@
Path path = compileIntermediate(globals);
testForD8()
.addProgramFiles(path)
- .apply(
+ .applyIf(
+ canUseNativeRecords(parameters),
+ b -> assertFalse(globals.hasGlobals()),
b ->
b.getBuilder()
.addGlobalSyntheticsResourceProviders(globals.getIndexedModeProvider()))
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java
index 8f33d07..214afea 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java
@@ -4,17 +4,14 @@
package com.android.tools.r8.desugar.records;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.errors.DuplicateTypeInProgramAndLibraryDiagnostic;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils;
import java.nio.file.Path;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,10 +36,13 @@
"true",
"false",
"false",
- "%s[name=Jane Doe, age=42]");
+ "%s[%s=Jane Doe, %s=42]");
private static final String EXPECTED_RESULT_D8 =
- String.format(EXPECTED_RESULT, "Empty", "Person");
- private static final String EXPECTED_RESULT_R8 = String.format(EXPECTED_RESULT, "a", "b");
+ String.format(EXPECTED_RESULT, "Empty", "Person", "name", "age");
+ private static final String EXPECTED_RESULT_R8 =
+ String.format(EXPECTED_RESULT, "a", "b", "name", "age");
+ private static final String EXPECTED_RESULT_R8_2 =
+ String.format(EXPECTED_RESULT, "a", "b", "a", "b");
private final TestParameters parameters;
@@ -80,32 +80,17 @@
.setMinApi(parameters)
.compile()
.writeToZip();
+ if (canUseNativeRecords(parameters)) {
+ assertFalse(ZipUtils.containsEntry(desugared, "com/android/tools/r8/RecordTag.class"));
+ } else {
+ assertTrue(ZipUtils.containsEntry(desugared, "com/android/tools/r8/RecordTag.class"));
+ }
testForR8(parameters.getBackend())
.addProgramFiles(desugared)
.setMinApi(parameters)
.addKeepMainRule(MAIN_TYPE)
- .applyIf(
- parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.U),
- b -> b.allowDiagnosticMessages())
- .compileWithExpectedDiagnostics(
- // Type com.android.tools.r8.RecordTag from desugared code will be converted to
- // java.lang.Record during reading causing duplicate java.lang.Record class.
- parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.U)
- ? diagnostics ->
- diagnostics
- .assertWarningsMatch(
- diagnosticMessage(
- containsString(
- "The following library types, prefixed by java., are present"
- + " both as library and non library classes:"
- + " java.lang.Record.")))
- .assertInfosMatch(
- allOf(
- diagnosticType(DuplicateTypeInProgramAndLibraryDiagnostic.class),
- diagnosticMessage(containsString("java.lang.Record"))))
- .assertNoErrors()
- : diagnostics -> diagnostics.assertNoMessages())
.run(parameters.getRuntime(), MAIN_TYPE)
- .assertSuccessWithOutput(EXPECTED_RESULT_R8);
+ .assertSuccessWithOutput(
+ canUseNativeRecords(parameters) ? EXPECTED_RESULT_R8_2 : EXPECTED_RESULT_R8);
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
index dfa8cd5..38954bf 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
@@ -6,12 +6,13 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8TestBuilder;
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.TestBase;
@@ -67,20 +68,25 @@
}
@Test
- public void testFailureWithoutGlobalSyntheticsConsumer() {
- assertThrows(
- CompilationFailedException.class,
- () ->
- testForD8(parameters.getBackend())
- .addProgramClassFileData(PROGRAM_DATA_1)
- .setMinApi(parameters)
- .setIntermediate(true)
- .compileWithExpectedDiagnostics(
- diagnostics ->
- diagnostics
- .assertOnlyErrors()
- .assertErrorsMatch(
- diagnosticType(MissingGlobalSyntheticsConsumerDiagnostic.class))));
+ public void testNoGlobalSyntheticsConsumer() throws Exception {
+ D8TestBuilder builder =
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA_1)
+ .setMinApi(parameters)
+ .setIntermediate(true);
+ if (canUseNativeRecords(parameters)) {
+ builder.compile();
+ } else {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ builder.compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics
+ .assertOnlyErrors()
+ .assertErrorsMatch(
+ diagnosticType(MissingGlobalSyntheticsConsumerDiagnostic.class))));
+ }
}
@Test
@@ -123,8 +129,8 @@
.inspect(this::assertDoesNotHaveRecordTag)
.writeToZip();
- assertTrue(globals1.hasGlobals());
- assertTrue(globals2.hasGlobals());
+ assertTrue(canUseNativeRecords(parameters) ^ globals1.hasGlobals());
+ assertTrue(canUseNativeRecords(parameters) ^ globals2.hasGlobals());
D8TestCompileResult result =
testForD8(parameters.getBackend())
@@ -138,8 +144,18 @@
.compile()
.inspect(this::assertHasRecordTag);
- result.run(parameters.getRuntime(), MAIN_TYPE_1).assertSuccessWithOutput(EXPECTED_RESULT_1);
- result.run(parameters.getRuntime(), MAIN_TYPE_2).assertSuccessWithOutput(EXPECTED_RESULT_2);
+ result
+ .run(parameters.getRuntime(), MAIN_TYPE_1)
+ .applyIf(
+ canUseNativeRecords(parameters) && !runtimeWithRecordsSupport(parameters.getRuntime()),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1));
+ result
+ .run(parameters.getRuntime(), MAIN_TYPE_2)
+ .applyIf(
+ canUseNativeRecords(parameters) && !runtimeWithRecordsSupport(parameters.getRuntime()),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT_2));
}
@Test
@@ -162,8 +178,18 @@
.addProgramClassFileData(PROGRAM_DATA_2)
.setMinApi(parameters)
.compile();
- result.run(parameters.getRuntime(), MAIN_TYPE_1).assertSuccessWithOutput(EXPECTED_RESULT_1);
- result.run(parameters.getRuntime(), MAIN_TYPE_2).assertSuccessWithOutput(EXPECTED_RESULT_2);
+ result
+ .run(parameters.getRuntime(), MAIN_TYPE_1)
+ .applyIf(
+ canUseNativeRecords(parameters) && !runtimeWithRecordsSupport(parameters.getRuntime()),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1));
+ result
+ .run(parameters.getRuntime(), MAIN_TYPE_2)
+ .applyIf(
+ canUseNativeRecords(parameters) && !runtimeWithRecordsSupport(parameters.getRuntime()),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT_2));
}
@Test
@@ -184,22 +210,44 @@
.inspect(this::assertHasRecordTag)
.writeToZip();
- assertThrows(
- CompilationFailedException.class,
- () ->
- testForD8(parameters.getBackend())
- .addProgramFiles(output1, output2)
- .setMinApi(parameters)
- .compileWithExpectedDiagnostics(
- diagnostics ->
- diagnostics
- .assertOnlyErrors()
- .assertErrorsMatch(diagnosticType(DuplicateTypesDiagnostic.class))));
+ if (canUseNativeRecords(parameters)) {
+ D8TestCompileResult result =
+ testForD8(parameters.getBackend())
+ .addProgramFiles(output1, output2)
+ .setMinApi(parameters)
+ .compile();
+ result
+ .run(parameters.getRuntime(), MAIN_TYPE_1)
+ .applyIf(
+ canUseNativeRecords(parameters)
+ && !runtimeWithRecordsSupport(parameters.getRuntime()),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1));
+ result
+ .run(parameters.getRuntime(), MAIN_TYPE_2)
+ .applyIf(
+ canUseNativeRecords(parameters)
+ && !runtimeWithRecordsSupport(parameters.getRuntime()),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT_2));
+ } else {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForD8(parameters.getBackend())
+ .addProgramFiles(output1, output2)
+ .setMinApi(parameters)
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics
+ .assertOnlyErrors()
+ .assertErrorsMatch(diagnosticType(DuplicateTypesDiagnostic.class))));
+ }
}
private void assertHasRecordTag(CodeInspector inspector) {
// Note: this should be asserting on record tag.
- assertThat(inspector.clazz("java.lang.Record"), isPresent());
+ assertThat(inspector.clazz("java.lang.Record"), isPresentIf(!canUseNativeRecords(parameters)));
}
private void assertDoesNotHaveRecordTag(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordWithNonMaterializableConstClassTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordWithNonMaterializableConstClassTest.java
index 0262140..428563a 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordWithNonMaterializableConstClassTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordWithNonMaterializableConstClassTest.java
@@ -34,6 +34,8 @@
private static final String EXPECTED_RESULT_D8 =
String.format(EXPECTED_RESULT_FORMAT, "MyRecordWithConstClass", "theClass");
private static final String EXPECTED_RESULT_R8 = String.format(EXPECTED_RESULT_FORMAT, "a", "a");
+ private static final String EXPECTED_RESULT_R8_ART14 =
+ String.format(EXPECTED_RESULT_FORMAT, "a", "theClass");
@Parameter(0)
public TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
index e208949..5ed96ab 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
@@ -4,18 +4,21 @@
package com.android.tools.r8.desugar.records;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.GlobalSyntheticsConsumer;
import com.android.tools.r8.R8FullTestBuilder;
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.synthesis.globals.GlobalSyntheticsTestingConsumer;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -45,9 +48,14 @@
@Parameter(0)
public TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ @Parameter(1)
+ public boolean forceInvokeRangeForInvokeCustom;
+
+ @Parameters(name = "{0}, forceInvokeRangeForInvokeCustom: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
+ BooleanUtils.values());
}
private boolean isCfRuntimeWithNativeRecordSupport() {
@@ -59,6 +67,7 @@
@Test
public void testReference() throws Exception {
assumeTrue(isCfRuntimeWithNativeRecordSupport());
+ assumeFalse(forceInvokeRangeForInvokeCustom);
testForJvm(parameters)
.addProgramClassFileData(PROGRAM_DATA)
.run(parameters.getRuntime(), MAIN_TYPE)
@@ -67,6 +76,8 @@
@Test
public void testD8() throws Exception {
+ assumeFalse(forceInvokeRangeForInvokeCustom);
+ boolean runningWithNativeRecordSupport = runtimeWithRecordsSupport(parameters.getRuntime());
testForD8(parameters.getBackend())
.addProgramClassFileData(PROGRAM_DATA)
.setMinApi(parameters)
@@ -75,17 +86,24 @@
RecordTestUtils::assertNoJavaLangRecord,
options -> options.testing.disableRecordApplicationReaderMap = true)
.run(parameters.getRuntime(), MAIN_TYPE)
- .assertSuccessWithOutput(EXPECTED_RESULT);
+ .applyIf(
+ canUseNativeRecords(parameters) && !runningWithNativeRecordSupport,
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT));
+ ;
}
@Test
public void testD8Intermediate() throws Exception {
assumeTrue(parameters.isDexRuntime());
+ assumeFalse(forceInvokeRangeForInvokeCustom);
GlobalSyntheticsTestingConsumer globals = new GlobalSyntheticsTestingConsumer();
Path path = compileIntermediate(globals);
testForD8()
.addProgramFiles(path)
- .apply(
+ .applyIf(
+ parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.U),
+ b -> assertFalse(globals.hasGlobals()),
b ->
b.getBuilder()
.addGlobalSyntheticsResourceProviders(globals.getIndexedModeProvider()))
@@ -98,12 +116,15 @@
@Test
public void testD8IntermediateNoDesugaringInStep2() throws Exception {
assumeTrue(parameters.isDexRuntime());
+ assumeFalse(forceInvokeRangeForInvokeCustom);
GlobalSyntheticsTestingConsumer globals = new GlobalSyntheticsTestingConsumer();
Path path = compileIntermediate(globals);
// In Android Studio they disable desugaring at this point to improve build speed.
testForD8()
.addProgramFiles(path)
- .apply(
+ .applyIf(
+ parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.U),
+ b -> assertFalse(globals.hasGlobals()),
b ->
b.getBuilder()
.addGlobalSyntheticsResourceProviders(globals.getIndexedModeProvider()))
@@ -130,8 +151,13 @@
public void testR8() throws Exception {
parameters.assumeR8TestParameters();
assumeTrue(parameters.isDexRuntime() || isCfRuntimeWithNativeRecordSupport());
+ assumeTrue(forceInvokeRangeForInvokeCustom || !parameters.isDexRuntime());
R8FullTestBuilder builder =
testForR8(parameters.getBackend())
+ .addOptionsModification(
+ opptions ->
+ opptions.testing.forceInvokeRangeForInvokeCustom =
+ forceInvokeRangeForInvokeCustom)
.addProgramClassFileData(PROGRAM_DATA)
.setMinApi(parameters)
.addKeepMainRule(MAIN_TYPE);
@@ -140,6 +166,10 @@
.addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
.compile()
.inspect(RecordTestUtils::assertRecordsAreRecords)
+ .inspect(
+ inspector -> {
+ inspector.clazz("records.SimpleRecord$Person").isRenamed();
+ })
.run(parameters.getRuntime(), MAIN_TYPE)
.assertSuccessWithOutput(EXPECTED_RESULT);
return;
@@ -157,6 +187,7 @@
public void testR8NoMinification() throws Exception {
parameters.assumeR8TestParameters();
assumeTrue(parameters.isDexRuntime() || isCfRuntimeWithNativeRecordSupport());
+ assumeTrue(forceInvokeRangeForInvokeCustom || !parameters.isDexRuntime());
R8FullTestBuilder builder =
testForR8(parameters.getBackend())
.addProgramClassFileData(PROGRAM_DATA)
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/PermittedSubclassesAttributeInDexTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/PermittedSubclassesAttributeInDexTest.java
index cc01489..4934f16 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/PermittedSubclassesAttributeInDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/PermittedSubclassesAttributeInDexTest.java
@@ -39,6 +39,7 @@
return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
+ private static final String EXPECTED_OUTPUT_PRE_34 = StringUtils.lines("false");
private static final String EXPECTED_OUTPUT = StringUtils.lines("true", "true");
@Test
@@ -56,9 +57,12 @@
private void inspect(CodeInspector inspector) {
assertEquals(
- ImmutableList.of(
- inspector.clazz(Sub1.class).asTypeSubject(),
- inspector.clazz(Sub2.class).asTypeSubject()),
+ parameters.getBackend().isDex()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.U)
+ ? ImmutableList.of(
+ inspector.clazz(Sub1.class).asTypeSubject(),
+ inspector.clazz(Sub2.class).asTypeSubject())
+ : ImmutableList.of(),
inspector.clazz(C.class).getFinalPermittedSubclassAttributes());
}
@@ -69,14 +73,17 @@
.addProgramClassFileData(getTransformedClasses())
.addProgramClasses(Sub1.class, Sub2.class)
.setMinApi(parameters)
- .addOptionsModification(options -> options.emitPermittedSubclassesAnnotationsInDex = true)
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
// TODO(b/270941147): Partial DEX support in Android U DP1 (reflective APIs).
parameters.isDexRuntimeVersionNewerThanOrEqual(Version.V14_0_0),
- r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
+ r ->
+ r.assertSuccessWithOutput(
+ parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.U)
+ ? EXPECTED_OUTPUT
+ : EXPECTED_OUTPUT_PRE_34),
r -> r.assertFailureWithErrorThatThrows(NoSuchMethodError.class));
}
@@ -135,10 +142,12 @@
public static void main(String[] args) {
System.out.println(AdditionalClassAPIs.isSealed(C.class));
- System.out.println(
- sameArrayContent(
- new Class<?>[] {Sub1.class, Sub2.class},
- AdditionalClassAPIs.getPermittedSubclasses(C.class)));
+ if (AdditionalClassAPIs.isSealed(C.class)) {
+ System.out.println(
+ sameArrayContent(
+ new Class<?>[] {Sub1.class, Sub2.class},
+ AdditionalClassAPIs.getPermittedSubclasses(C.class)));
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index 592281a..31b779c 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -19,6 +19,7 @@
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.desugar.records.RecordTestUtils;
import com.android.tools.r8.profile.art.model.ExternalArtProfile;
import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
@@ -78,7 +79,7 @@
@Test
public void testReference() throws Exception {
parameters.assumeJvmTestParameters();
- assumeTrue(parameters.canUseRecords());
+ assumeTrue(runtimeWithRecordsSupport(parameters.getRuntime()));
testForJvm(parameters)
.addProgramClassFileData(PROGRAM_DATA)
.run(parameters.getRuntime(), MAIN_REFERENCE.getTypeName())
@@ -100,13 +101,16 @@
inspector -> inspectD8(profileInspector, inspector),
options -> options.testing.disableRecordApplicationReaderMap = true))
.run(parameters.getRuntime(), MAIN_REFERENCE.getTypeName())
- .assertSuccessWithOutput(EXPECTED_RESULT);
+ .applyIf(
+ canUseNativeRecords(parameters) && !runtimeWithRecordsSupport(parameters.getRuntime()),
+ r -> r.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT));
}
@Test
public void testR8() throws Exception {
parameters.assumeR8TestParameters();
- assumeTrue(parameters.canUseRecords() || parameters.isDexRuntime());
+ assumeTrue(runtimeWithRecordsSupport(parameters.getRuntime()) || parameters.isDexRuntime());
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addProgramClassFileData(PROGRAM_DATA)
@@ -162,7 +166,7 @@
SyntheticItemsTestUtils.syntheticRecordTagClass(),
false,
parameters.canUseNestBasedAccessesWhenDesugaring(),
- parameters.canUseRecordsWhenDesugaring());
+ canUseNativeRecords(parameters));
}
private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
@@ -172,7 +176,7 @@
RECORD_REFERENCE,
parameters.canHaveNonReboundConstructorInvoke(),
parameters.canUseNestBasedAccesses(),
- parameters.canUseRecords());
+ canUseNativeRecords(parameters) || parameters.isCfRuntime());
}
private void inspect(
@@ -207,7 +211,15 @@
? inspector.getTypeSubject(RECORD_REFERENCE.getTypeName())
: recordTagClassSubject.asTypeSubject(),
personRecordClassSubject.getSuperType());
- assertEquals(canUseRecords ? 6 : 10, personRecordClassSubject.allMethods().size());
+ assertEquals(
+ canUseRecords
+ ? (parameters.isCfRuntime()
+ && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17)
+ && !canUseNativeRecords(parameters)
+ ? 6
+ : 8)
+ : 10,
+ personRecordClassSubject.allMethods().size());
MethodSubject personInstanceInitializerSubject =
personRecordClassSubject.uniqueInstanceInitializer();