|  | // 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; | 
|  | } | 
|  | } |