|  | // 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.naming; | 
|  |  | 
|  | import static com.android.tools.r8.utils.DescriptorUtils.descriptorToInternalName; | 
|  | import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType; | 
|  | import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor; | 
|  |  | 
|  | import com.android.tools.r8.graph.DexType; | 
|  | import com.android.tools.r8.naming.MemberNaming.Signature; | 
|  | import com.android.tools.r8.position.Position; | 
|  | import com.android.tools.r8.utils.Reporter; | 
|  | import com.google.common.collect.ImmutableMap; | 
|  | import java.io.BufferedReader; | 
|  | import java.io.IOException; | 
|  | import java.io.InputStream; | 
|  | import java.io.InputStreamReader; | 
|  | import java.nio.charset.StandardCharsets; | 
|  | import java.nio.file.Files; | 
|  | import java.nio.file.Path; | 
|  | import java.util.HashMap; | 
|  | import java.util.HashSet; | 
|  | import java.util.Map; | 
|  | import java.util.Set; | 
|  |  | 
|  | /** | 
|  | * Mappings read from the given ProGuard map. | 
|  | * <p> | 
|  | * The main differences of this against {@link ClassNameMapper} and | 
|  | * {@link ClassNameMapper#getObfuscatedToOriginalMapping()} are: | 
|  | *   1) the key is the original descriptor, not the obfuscated java name. Thus, it is much easier | 
|  | *   to look up what mapping to apply while traversing {@link DexType}s; and | 
|  | *   2) the value is {@link ClassNamingForMapApplier}, another variant of {@link ClassNaming}, | 
|  | *   which also uses original {@link Signature} as a key, instead of renamed {@link Signature}. | 
|  | */ | 
|  | public class SeedMapper implements ProguardMap { | 
|  |  | 
|  | static class Builder extends ProguardMap.Builder { | 
|  | final Map<String, ClassNamingForMapApplier.Builder> map = new HashMap<>(); | 
|  | final Set<String> mappedToDescriptorNames = new HashSet<>(); | 
|  | private final Reporter reporter; | 
|  |  | 
|  | private Builder(Reporter reporter) { | 
|  | this.reporter = reporter; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | ClassNamingForMapApplier.Builder classNamingBuilder( | 
|  | String renamedName, String originalName, Position position) { | 
|  | String originalDescriptor = javaTypeToDescriptor(originalName); | 
|  | String renamedDescriptorName = javaTypeToDescriptor(renamedName); | 
|  | mappedToDescriptorNames.add(renamedDescriptorName); | 
|  | ClassNamingForMapApplier.Builder classNamingBuilder = | 
|  | ClassNamingForMapApplier.builder( | 
|  | renamedDescriptorName, originalDescriptor, position, reporter); | 
|  | if (map.put(originalDescriptor, classNamingBuilder) != null) { | 
|  | reporter.error(ProguardMapError.duplicateSourceClass(originalName, position)); | 
|  | } | 
|  | return classNamingBuilder; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | SeedMapper build() { | 
|  | reporter.failIfPendingErrors(); | 
|  | return new SeedMapper(ImmutableMap.copyOf(map), mappedToDescriptorNames, reporter); | 
|  | } | 
|  | } | 
|  |  | 
|  | static Builder builder(Reporter reporter) { | 
|  | return new Builder(reporter); | 
|  | } | 
|  |  | 
|  | private static SeedMapper seedMapperFromInputStream(Reporter reporter, InputStream in) | 
|  | throws IOException { | 
|  | BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); | 
|  | try (ProguardMapReader proguardReader = new ProguardMapReader(reader, reporter, false, false)) { | 
|  | SeedMapper.Builder builder = SeedMapper.builder(reporter); | 
|  | proguardReader.parse(builder); | 
|  | return builder.build(); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static SeedMapper seedMapperFromFile(Reporter reporter, Path path) throws IOException { | 
|  | return seedMapperFromInputStream(reporter, Files.newInputStream(path)); | 
|  | } | 
|  |  | 
|  | private final ImmutableMap<String, ClassNamingForMapApplier> mappings; | 
|  | private final Set<String> mappedToDescriptorNames; | 
|  | private final Reporter reporter; | 
|  |  | 
|  | private SeedMapper( | 
|  | Map<String, ClassNamingForMapApplier.Builder> mappings, | 
|  | Set<String> mappedToDescriptorNames, | 
|  | Reporter reporter) { | 
|  | this.reporter = reporter; | 
|  | ImmutableMap.Builder<String, ClassNamingForMapApplier> builder = ImmutableMap.builder(); | 
|  | for(Map.Entry<String, ClassNamingForMapApplier.Builder> entry : mappings.entrySet()) { | 
|  | builder.put(entry.getKey(), entry.getValue().build()); | 
|  | } | 
|  | this.mappings = builder.build(); | 
|  | this.mappedToDescriptorNames = mappedToDescriptorNames; | 
|  | verifyMappingsAreConflictFree(); | 
|  | } | 
|  |  | 
|  | private void verifyMappingsAreConflictFree() { | 
|  | Map<String, String> seenMappings = new HashMap<>(); | 
|  | for (String key : mappings.keySet()) { | 
|  | ClassNamingForMapApplier classNaming = mappings.get(key); | 
|  | String existing = seenMappings.put(classNaming.renamedName, key); | 
|  | if (existing != null) { | 
|  | reporter.error( | 
|  | ProguardMapError.duplicateTargetClass( | 
|  | descriptorToJavaType(key), | 
|  | descriptorToJavaType(existing), | 
|  | descriptorToInternalName(classNaming.renamedName), | 
|  | classNaming.position)); | 
|  | } | 
|  | // TODO(b/136694827) Enable when we have proper support | 
|  | // Map<Signature, MemberNaming> seenMembers = new HashMap<>(); | 
|  | // classNaming.forAllMemberNaming( | 
|  | //     memberNaming -> { | 
|  | //       MemberNaming existingMember = | 
|  | //           seenMembers.put(memberNaming.renamedSignature, memberNaming); | 
|  | //       if (existingMember != null) { | 
|  | //         reporter.error( | 
|  | //             ProguardMapError.duplicateTargetSignature( | 
|  | //                 existingMember.signature, | 
|  | //                 memberNaming.signature, | 
|  | //                 memberNaming.getRenamedName(), | 
|  | //                 memberNaming.position)); | 
|  | //       } | 
|  | //     }); | 
|  | } | 
|  | reporter.failIfPendingErrors(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean hasMapping(DexType type) { | 
|  | return mappings.containsKey(type.descriptor.toString()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ClassNamingForMapApplier getClassNaming(DexType type) { | 
|  | return mappings.get(type.descriptor.toString()); | 
|  | } | 
|  |  | 
|  | public Set<String> getKeyset() { | 
|  | return mappings.keySet(); | 
|  | } | 
|  |  | 
|  | public Set<String> getMappedToDescriptorNames() { | 
|  | return mappedToDescriptorNames; | 
|  | } | 
|  |  | 
|  | public ClassNamingForMapApplier getMapping(String key) { | 
|  | return mappings.get(key); | 
|  | } | 
|  | } |