Fix incorrect equals() implementation in record desugaring
Bug: b/289237734
Change-Id: I03a35dc2aec5f4abba5f9b664f3f9ffc9132e50d
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/RecordCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/RecordCfCodeProvider.java
index 6602826..5abcb38 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/RecordCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/RecordCfCodeProvider.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.cf.code.CfCheckCast;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfIf;
import com.android.tools.r8.cf.code.CfIfCmp;
import com.android.tools.r8.cf.code.CfInstanceFieldRead;
import com.android.tools.r8.cf.code.CfInstruction;
@@ -153,6 +154,9 @@
@Override
public CfCode generateCfCode() {
// This generates something along the lines of:
+ // if (other == null) {
+ // return false;
+ // }
// if (this.getClass() != other.getClass()) {
// return false;
// }
@@ -160,10 +164,22 @@
// recordInstance.getFieldsAsObjects(),
// ((RecordClass) other).getFieldsAsObjects());
DexItemFactory factory = appView.dexItemFactory();
- List<CfInstruction> instructions = new ArrayList<>();
+ int numberOfInstructions = 22;
+ List<CfInstruction> instructions = new ArrayList<>(numberOfInstructions);
+ CfLabel notNullLabel = new CfLabel();
CfLabel fieldCmp = new CfLabel();
ValueType recordType = ValueType.fromDexType(getHolder());
ValueType objectType = ValueType.fromDexType(factory.objectType);
+ instructions.add(new CfLoad(objectType, 1));
+ instructions.add(new CfIf(IfType.NE, ValueType.OBJECT, notNullLabel));
+ instructions.add(new CfConstNumber(0, ValueType.INT));
+ instructions.add(new CfReturn(ValueType.INT));
+ instructions.add(notNullLabel);
+ instructions.add(
+ CfFrame.builder()
+ .appendLocal(FrameType.initialized(getHolder()))
+ .appendLocal(FrameType.initialized(appView.dexItemFactory().objectType))
+ .build());
instructions.add(new CfLoad(recordType, 0));
instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, factory.objectMembers.getClass, false));
instructions.add(new CfLoad(objectType, 1));
@@ -186,6 +202,7 @@
new CfInvoke(
Opcodes.INVOKESTATIC, factory.javaUtilArraysMethods.equalsObjectArray, false));
instructions.add(new CfReturn(ValueType.INT));
+ assert instructions.size() == numberOfInstructions;
return standardCfCodeFromInstructions(instructions);
}
}
diff --git a/src/test/examplesJava17/records/SimpleRecord.java b/src/test/examplesJava17/records/SimpleRecord.java
index 1f8fca4..3f908dc 100644
--- a/src/test/examplesJava17/records/SimpleRecord.java
+++ b/src/test/examplesJava17/records/SimpleRecord.java
@@ -14,5 +14,22 @@
System.out.println(janeDoe.age);
System.out.println(janeDoe.name());
System.out.println(janeDoe.age());
+
+ // Test equals with self.
+ System.out.println(janeDoe.equals(janeDoe));
+
+ // Test equals with structurally equals Person.
+ Person otherJaneDoe = new Person("Jane Doe", 42);
+ System.out.println(janeDoe.equals(otherJaneDoe));
+ System.out.println(otherJaneDoe.equals(janeDoe));
+
+ // Test equals with not-structually equals Person.
+ Person johnDoe = new Person("John Doe", 42);
+ System.out.println(janeDoe.equals(johnDoe));
+ System.out.println(johnDoe.equals(janeDoe));
+
+ // Test equals with Object and null.
+ System.out.println(janeDoe.equals(new Object()));
+ System.out.println(janeDoe.equals(null));
}
}
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 fb80241..dfa8cd5 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
@@ -42,7 +42,18 @@
private static final byte[][] PROGRAM_DATA_2 = RecordTestUtils.getProgramData(RECORD_NAME_2);
private static final String MAIN_TYPE_2 = RecordTestUtils.getMainType(RECORD_NAME_2);
private static final String EXPECTED_RESULT_2 =
- StringUtils.lines("Jane Doe", "42", "Jane Doe", "42");
+ StringUtils.lines(
+ "Jane Doe",
+ "42",
+ "Jane Doe",
+ "42",
+ "true",
+ "true",
+ "true",
+ "false",
+ "false",
+ "false",
+ "false");
private final 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 7dbeae0..e208949 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
@@ -29,7 +29,18 @@
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 =
- StringUtils.lines("Jane Doe", "42", "Jane Doe", "42");
+ StringUtils.lines(
+ "Jane Doe",
+ "42",
+ "Jane Doe",
+ "42",
+ "true",
+ "true",
+ "true",
+ "false",
+ "false",
+ "false",
+ "false");
@Parameter(0)
public TestParameters parameters;
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 6d03704..2577312 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
@@ -45,7 +45,18 @@
private static final String RECORD_NAME = "SimpleRecord";
private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
private static final String EXPECTED_RESULT =
- StringUtils.lines("Jane Doe", "42", "Jane Doe", "42");
+ StringUtils.lines(
+ "Jane Doe",
+ "42",
+ "Jane Doe",
+ "42",
+ "true",
+ "true",
+ "true",
+ "false",
+ "false",
+ "false",
+ "false");
private static final ClassReference MAIN_REFERENCE =
Reference.classFromTypeName(RecordTestUtils.getMainType(RECORD_NAME));