diff options
author | Clément Béra <clementbera@google.com> | 2021-03-15 15:46:55 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-03-15 15:46:55 +0000 |
commit | 25c3482eeeedcc7a2dd5a9ac048616fa9e02449c (patch) | |
tree | 4f0a9cf300a8e7b82118e857668eb52f47699307 | |
parent | 7340f9474225a361d68fe624c18719759cb02477 (diff) | |
parent | 8eca109aa3e72aefa66c28eb3daef548b8dc4b86 (diff) | |
download | r8-25c3482eeeedcc7a2dd5a9ac048616fa9e02449c.tar.gz |
Support Record dex merge am: 8eca109aa3
Original change: undetermined
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: I757b09ddfaca216f9c66aad990d289597a9d502b
9 files changed, 174 insertions, 15 deletions
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java index cd789952c..26bbc5e96 100644 --- a/src/main/java/com/android/tools/r8/dex/DexParser.java +++ b/src/main/java/com/android/tools/r8/dex/DexParser.java @@ -12,6 +12,7 @@ import com.android.tools.r8.ProgramResource.Kind; import com.android.tools.r8.code.Instruction; import com.android.tools.r8.code.InstructionFactory; import com.android.tools.r8.errors.CompilationError; +import com.android.tools.r8.graph.ApplicationReaderMap; import com.android.tools.r8.graph.ClassAccessFlags; import com.android.tools.r8.graph.ClassKind; import com.android.tools.r8.graph.DexAnnotation; @@ -79,6 +80,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.function.Consumer; import java.util.function.Supplier; @@ -992,9 +994,12 @@ public class DexParser<T extends DexClass> { private void populateTypes() { DexSection dexSection = lookupSection(Constants.TYPE_TYPE_ID_ITEM); assert verifyOrderOfTypeIds(dexSection); + Map<DexType, DexType> typeMap = ApplicationReaderMap.getTypeMap(options); indexedItems.initializeTypes(dexSection.length); for (int i = 0; i < dexSection.length; i++) { - indexedItems.setType(i, typeAt(i)); + DexType type = typeAt(i); + DexType actualType = typeMap.getOrDefault(type, type); + indexedItems.setType(i, actualType); } } diff --git a/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java new file mode 100644 index 000000000..82b9d7f9f --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java @@ -0,0 +1,31 @@ +// 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.graph; + +import com.android.tools.r8.utils.InternalOptions; +import com.google.common.collect.ImmutableMap; +import java.util.Map; + +public class ApplicationReaderMap { + + public static Map<String, String> getDescriptorMap(InternalOptions options) { + ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); + if (options.shouldDesugarRecords()) { + builder.put(DexItemFactory.recordTagDescriptorString, DexItemFactory.recordDescriptorString); + } + return builder.build(); + } + + public static Map<DexType, DexType> getTypeMap(InternalOptions options) { + DexItemFactory factory = options.dexItemFactory(); + ImmutableMap.Builder<DexType, DexType> builder = ImmutableMap.builder(); + getDescriptorMap(options) + .forEach( + (k, v) -> { + builder.put(factory.createType(k), factory.createType(v)); + }); + return builder.build(); + } +} 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 1339701a7..8ba291b93 100644 --- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java +++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java @@ -64,6 +64,8 @@ public class DexItemFactory { public static final String throwableDescriptorString = "Ljava/lang/Throwable;"; public static final String dalvikAnnotationSignatureString = "Ldalvik/annotation/Signature;"; + public static final String recordTagDescriptorString = "Lcom/android/tools/r8/RecordTag;"; + public static final String recordDescriptorString = "Ljava/lang/Record;"; /** Set of types that may be synthesized during compilation. */ private final Set<DexType> possibleCompilerSynthesizedTypes = Sets.newIdentityHashSet(); @@ -215,8 +217,8 @@ public class DexItemFactory { public final DexString stringDescriptor = createString("Ljava/lang/String;"); public final DexString stringArrayDescriptor = createString("[Ljava/lang/String;"); public final DexString objectDescriptor = createString("Ljava/lang/Object;"); - public final DexString recordDescriptor = createString("Ljava/lang/Record;"); - public final DexString r8RecordDescriptor = createString("Lcom/android/tools/r8/RecordTag;"); + public final DexString recordDescriptor = createString(recordDescriptorString); + public final DexString recordTagDescriptor = createString(recordTagDescriptorString); public final DexString objectArrayDescriptor = createString("[Ljava/lang/Object;"); public final DexString classDescriptor = createString("Ljava/lang/Class;"); public final DexString classLoaderDescriptor = createString("Ljava/lang/ClassLoader;"); @@ -348,7 +350,7 @@ public class DexItemFactory { public final DexType stringArrayType = createStaticallyKnownType(stringArrayDescriptor); public final DexType objectType = createStaticallyKnownType(objectDescriptor); public final DexType recordType = createStaticallyKnownType(recordDescriptor); - public final DexType r8RecordType = createStaticallyKnownType(r8RecordDescriptor); + public final DexType recordTagType = createStaticallyKnownType(recordTagDescriptor); public final DexType objectArrayType = createStaticallyKnownType(objectArrayDescriptor); public final DexType classArrayType = createStaticallyKnownType(classArrayDescriptor); public final DexType enumType = createStaticallyKnownType(enumDescriptor); diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java index 75b592b2d..a38770a70 100644 --- a/src/main/java/com/android/tools/r8/graph/DexType.java +++ b/src/main/java/com/android/tools/r8/graph/DexType.java @@ -315,6 +315,14 @@ public class DexType extends DexReference implements NamingLensComparable<DexTyp return isDoubleType() || isLongType(); } + public boolean isSynthesizedTypeAllowedDuplication() { + // If we are desugaring Records, then the r8Record type is mapped back to java.lang.Record, and + // java.lang.Record can be duplicated. + // If we are not desugaring Records, then the r8Record type can be duplicated instead. + return descriptor.toString().equals(DexItemFactory.recordDescriptorString) + || descriptor.toString().equals(DexItemFactory.recordTagDescriptorString); + } + public boolean isLegacySynthesizedTypeAllowedDuplication() { return oldSynthesizedName(toSourceString()); } diff --git a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java index 8b5977f83..67e7b5ba1 100644 --- a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java +++ b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java @@ -7,6 +7,7 @@ import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.InternalOptions; import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.objectweb.asm.Type; @@ -23,9 +24,11 @@ public class JarApplicationReader { private final ConcurrentHashMap<String, Type> asmObjectTypeCache = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, Type> asmTypeCache = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, DexString> stringCache = new ConcurrentHashMap<>(); + private final Map<String, String> typeDescriptorMap; public JarApplicationReader(InternalOptions options) { this.options = options; + typeDescriptorMap = ApplicationReaderMap.getDescriptorMap(options); } public Type getAsmObjectType(String name) { @@ -55,7 +58,8 @@ public class JarApplicationReader { public DexType getTypeFromDescriptor(String desc) { assert isValidDescriptor(desc); - return options.itemFactory.createType(getString(desc)); + String actualDesc = typeDescriptorMap.getOrDefault(desc, desc); + return options.itemFactory.createType(getString(actualDesc)); } public DexTypeList getTypeListFromNames(String[] names) { diff --git a/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java index d94a74700..469f119fe 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java @@ -191,7 +191,7 @@ public class RecordRewriter implements CfInstructionDesugaring, CfClassDesugarin null, true); encodedMethod.setCode( - new RecordGetFieldsAsObjectsCfCodeProvider(appView, factory.r8RecordType, fields) + new RecordGetFieldsAsObjectsCfCodeProvider(appView, factory.recordTagType, fields) .generateCfCode(), appView); return new ProgramMethod(clazz, encodedMethod); @@ -316,7 +316,6 @@ public class RecordRewriter implements CfInstructionDesugaring, CfClassDesugarin @Override public boolean needsDesugaring(DexProgramClass clazz) { - assert clazz.isRecord() || clazz.superType != factory.recordType; return clazz.isRecord(); } @@ -460,6 +459,17 @@ public class RecordRewriter implements CfInstructionDesugaring, CfClassDesugarin private DexProgramClass synthesizeR8Record() { DexItemFactory factory = appView.dexItemFactory(); + DexClass r8RecordClass = + appView.appInfo().definitionForWithoutExistenceAssert(factory.recordTagType); + if (r8RecordClass != null && r8RecordClass.isProgramClass()) { + appView + .options() + .reporter + .error( + "D8/R8 is compiling a mix of desugared and non desugared input using" + + " java.lang.Record, but the application reader did not import correctly " + + factory.recordTagType.toString()); + } DexClass recordClass = appView.appInfo().definitionForWithoutExistenceAssert(factory.recordType); if (recordClass != null && recordClass.isProgramClass()) { @@ -520,7 +530,7 @@ public class RecordRewriter implements CfInstructionDesugaring, CfClassDesugarin null, true); init.setCode( - new CallObjectInitCfCodeProvider(appView, factory.r8RecordType).generateCfCode(), appView); + new CallObjectInitCfCodeProvider(appView, factory.recordTagType).generateCfCode(), appView); return init; } } diff --git a/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java index 83fa84e51..e809317af 100644 --- a/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java +++ b/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java @@ -45,7 +45,7 @@ public class RecordRewritingNamingLens extends NonIdentityNamingLens { private DexString getRenaming(DexType type) { if (type == factory.recordType) { - return factory.r8RecordType.descriptor; + return factory.recordTagType.descriptor; } return null; } @@ -77,7 +77,7 @@ public class RecordRewritingNamingLens extends NonIdentityNamingLens { @Override public DexString lookupDescriptorForJavaTypeName(String typeName) { if (typeName.equals(factory.recordType.toSourceString())) { - return factory.r8RecordType.descriptor; + return factory.recordTagType.descriptor; } return namingLens.lookupDescriptorForJavaTypeName(typeName); } diff --git a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java index fff5425e8..8c3e592de 100644 --- a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java +++ b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java @@ -62,10 +62,7 @@ public class ProgramClassCollection extends ClassMap<DexProgramClass> { // All other conflicts are reported as a fatal error. return (DexProgramClass a, DexProgramClass b) -> { assert a.type == b.type; - if (a.originatesFromDexResource() - && b.originatesFromDexResource() - && a.accessFlags.isSynthetic() - && b.accessFlags.isSynthetic()) { + if (a.accessFlags.isSynthetic() && b.accessFlags.isSynthetic()) { return mergeClasses(reporter, a, b); } throw reportDuplicateTypes(reporter, a, b); @@ -82,7 +79,8 @@ public class ProgramClassCollection extends ClassMap<DexProgramClass> { private static DexProgramClass mergeClasses( Reporter reporter, DexProgramClass a, DexProgramClass b) { - if (a.type.isLegacySynthesizedTypeAllowedDuplication()) { + if (a.type.isLegacySynthesizedTypeAllowedDuplication() + || a.type.isSynthesizedTypeAllowedDuplication()) { assert assertEqualClasses(a, b); return a; } 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 new file mode 100644 index 000000000..bd08a25bd --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java @@ -0,0 +1,101 @@ +// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.desugar.records; + +import com.android.tools.r8.D8TestCompileResult; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestRuntime.CfRuntime; +import com.android.tools.r8.utils.InternalOptions.TestingOptions; +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; + +@RunWith(Parameterized.class) +public class RecordMergeTest extends TestBase { + + private static final String RECORD_NAME_1 = "RecordWithMembers"; + private static final byte[][] PROGRAM_DATA_1 = RecordTestUtils.getProgramData(RECORD_NAME_1); + private static final String MAIN_TYPE_1 = RecordTestUtils.getMainType(RECORD_NAME_1); + private static final String EXPECTED_RESULT_1 = + StringUtils.lines( + "BobX", "43", "BobX", "43", "FelixX", "-1", "FelixX", "-1", "print", "Bob43", "extra"); + + private static final String RECORD_NAME_2 = "SimpleRecord"; + 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"); + + private final TestParameters parameters; + + public RecordMergeTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Parameterized.Parameters(name = "{0}") + public static List<Object[]> data() { + // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15). + return buildParameters( + getTestParameters() + .withCustomRuntime(CfRuntime.getCheckedInJdk15()) + .withDexRuntimes() + .withAllApiLevelsAlsoForCf() + .build()); + } + + @Test + public void testMergeDesugaredInputs() throws Exception { + Path output1 = + testForD8(parameters.getBackend()) + .addProgramClassFileData(PROGRAM_DATA_1) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) + .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) + .compile() + .writeToZip(); + Path output2 = + testForD8(parameters.getBackend()) + .addProgramClassFileData(PROGRAM_DATA_2) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) + .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) + .compile() + .writeToZip(); + D8TestCompileResult result = + testForD8(parameters.getBackend()) + .addProgramFiles(output1, output2) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) + .compile(); + result.run(parameters.getRuntime(), MAIN_TYPE_1).assertSuccessWithOutput(EXPECTED_RESULT_1); + result.run(parameters.getRuntime(), MAIN_TYPE_2).assertSuccessWithOutput(EXPECTED_RESULT_2); + } + + @Test + public void testMergeDesugaredAndNonDesugaredInputs() throws Exception { + Path output1 = + testForD8(parameters.getBackend()) + .addProgramClassFileData(PROGRAM_DATA_1) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) + .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) + .compile() + .writeToZip(); + D8TestCompileResult result = + testForD8(parameters.getBackend()) + .addProgramFiles(output1) + .addProgramClassFileData(PROGRAM_DATA_2) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) + .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) + .compile(); + result.run(parameters.getRuntime(), MAIN_TYPE_1).assertSuccessWithOutput(EXPECTED_RESULT_1); + result.run(parameters.getRuntime(), MAIN_TYPE_2).assertSuccessWithOutput(EXPECTED_RESULT_2); + } +} |