blob: dee503bb423b86c63efea8de78d9d5431f93d6b9 [file] [log] [blame]
// 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);
}
}