| // Copyright (c) 2017, 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.utils; | |
| import com.android.tools.r8.ClassConflictResolver; | |
| import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver; | |
| import com.android.tools.r8.errors.DuplicateTypesDiagnostic; | |
| import com.android.tools.r8.graph.ClassKind; | |
| import com.android.tools.r8.graph.DexProgramClass; | |
| import com.android.tools.r8.graph.DexType; | |
| import com.android.tools.r8.origin.Origin; | |
| import com.android.tools.r8.references.Reference; | |
| import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramProvider.GlobalsEntryOrigin; | |
| import com.google.common.collect.ImmutableList; | |
| import java.util.ArrayList; | |
| import java.util.List; | |
| import java.util.concurrent.ConcurrentHashMap; | |
| import java.util.function.Supplier; | |
| /** Represents a collection of library classes. */ | |
| public class ProgramClassCollection extends ClassMap<DexProgramClass> { | |
| private final ProgramClassConflictResolver conflictResolver; | |
| public static ProgramClassCollection create( | |
| List<DexProgramClass> classes, ProgramClassConflictResolver conflictResolver) { | |
| // We have all classes preloaded, but not necessarily without conflicts. | |
| ConcurrentHashMap<DexType, Supplier<DexProgramClass>> map = new ConcurrentHashMap<>(); | |
| for (DexProgramClass clazz : classes) { | |
| map.merge( | |
| clazz.type, clazz, (a, b) -> conflictResolver.resolveClassConflict(a.get(), b.get())); | |
| } | |
| return new ProgramClassCollection(map, conflictResolver); | |
| } | |
| private ProgramClassCollection( | |
| ConcurrentHashMap<DexType, Supplier<DexProgramClass>> classes, | |
| ProgramClassConflictResolver conflictResolver) { | |
| super(classes, null); | |
| this.conflictResolver = conflictResolver; | |
| } | |
| @Override | |
| public String toString() { | |
| return "program classes: " + super.toString(); | |
| } | |
| @Override | |
| DexProgramClass resolveClassConflict(DexProgramClass a, DexProgramClass b) { | |
| return conflictResolver.resolveClassConflict(a, b); | |
| } | |
| @Override | |
| Supplier<DexProgramClass> getTransparentSupplier(DexProgramClass clazz) { | |
| return clazz; | |
| } | |
| @Override | |
| ClassKind<DexProgramClass> getClassKind() { | |
| return ClassKind.PROGRAM; | |
| } | |
| public static ProgramClassConflictResolver defaultConflictResolver(Reporter reporter) { | |
| // The default conflict resolver only merges synthetic classes generated by D8 correctly. | |
| // All other conflicts are reported as a fatal error. | |
| return wrappedConflictResolver(null, reporter); | |
| } | |
| public static ProgramClassConflictResolver wrappedConflictResolver( | |
| ClassConflictResolver clientResolver, Reporter reporter) { | |
| return (a, b) -> { | |
| DexProgramClass clazz = mergeClasses(a, b); | |
| if (clazz != null) { | |
| return clazz; | |
| } | |
| if (clientResolver != null) { | |
| List<Origin> origins = new ArrayList<>(); | |
| origins.add(a.getOrigin()); | |
| origins.add(b.getOrigin()); | |
| Origin origin = | |
| clientResolver.resolveDuplicateClass(a.getClassReference(), origins, reporter); | |
| if (origin == a.getOrigin()) { | |
| return a; | |
| } | |
| if (origin == b.getOrigin()) { | |
| return b; | |
| } | |
| } | |
| throw reportDuplicateTypes(reporter, a, b); | |
| }; | |
| } | |
| private static RuntimeException reportDuplicateTypes( | |
| Reporter reporter, DexProgramClass a, DexProgramClass b) { | |
| throw reporter.fatalError( | |
| new DuplicateTypesDiagnostic( | |
| Reference.classFromDescriptor(a.type.toDescriptorString()), | |
| ImmutableList.of(a.getOrigin(), b.getOrigin()))); | |
| } | |
| private static DexProgramClass mergeClasses(DexProgramClass a, DexProgramClass b) { | |
| assert a.type == b.type; | |
| boolean syntheticA = a.accessFlags.isSynthetic(); | |
| boolean syntheticB = b.accessFlags.isSynthetic(); | |
| if (syntheticA && syntheticB) { | |
| return mergeIfLegacySynthetics(a, b); | |
| } else if (syntheticA) { | |
| return mergeIfGlobalSynthetic(a, b); | |
| } else if (syntheticB) { | |
| return mergeIfGlobalSynthetic(b, a); | |
| } | |
| return null; | |
| } | |
| private static DexProgramClass mergeIfGlobalSynthetic( | |
| DexProgramClass synthetic, DexProgramClass nonSynthetic) { | |
| assert synthetic.accessFlags.isSynthetic(); | |
| assert !nonSynthetic.accessFlags.isSynthetic(); | |
| if (synthetic.getOrigin() instanceof GlobalsEntryOrigin) { | |
| return nonSynthetic; | |
| } | |
| return null; | |
| } | |
| private static DexProgramClass mergeIfLegacySynthetics(DexProgramClass a, DexProgramClass b) { | |
| if (a.type.isLegacySynthesizedTypeAllowedDuplication()) { | |
| assert assertEqualClasses(a, b); | |
| return a; | |
| } | |
| return null; | |
| } | |
| private static boolean assertEqualClasses(DexProgramClass a, DexProgramClass b) { | |
| assert a.getMethodCollection().numberOfDirectMethods() | |
| == b.getMethodCollection().numberOfDirectMethods(); | |
| assert a.getMethodCollection().size() == b.getMethodCollection().size(); | |
| return true; | |
| } | |
| } |