| // 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.ir.desugar.desugaredlibrary.humanspecification; |
| |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.MethodAccessFlags; |
| import com.android.tools.r8.origin.Origin; |
| import com.android.tools.r8.utils.Reporter; |
| import com.android.tools.r8.utils.StringDiagnostic; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Sets; |
| import com.google.common.collect.Sets.SetView; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.IdentityHashMap; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| public class HumanRewritingFlags { |
| |
| private final Map<String, String> rewritePrefix; |
| private final Map<String, Map<String, String>> rewriteDerivedPrefix; |
| private final Map<DexType, DexType> emulatedInterfaces; |
| private final Map<DexMethod, DexType> retargetMethod; |
| private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch; |
| private final Map<DexType, DexType> legacyBackport; |
| private final Map<DexType, DexType> customConversions; |
| private final Set<DexMethod> dontRewriteInvocation; |
| private final Set<DexType> dontRetarget; |
| private final Map<DexType, Set<DexMethod>> wrapperConversions; |
| private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod; |
| |
| HumanRewritingFlags( |
| Map<String, String> rewritePrefix, |
| Map<String, Map<String, String>> rewriteDerivedPrefix, |
| Map<DexType, DexType> emulateLibraryInterface, |
| Map<DexMethod, DexType> retargetMethod, |
| Map<DexMethod, DexType> retargetMethodEmulatedDispatch, |
| Map<DexType, DexType> legacyBackport, |
| Map<DexType, DexType> customConversion, |
| Set<DexMethod> dontRewriteInvocation, |
| Set<DexType> dontRetarget, |
| Map<DexType, Set<DexMethod>> wrapperConversion, |
| Map<DexMethod, MethodAccessFlags> amendLibraryMethod) { |
| this.rewritePrefix = rewritePrefix; |
| this.rewriteDerivedPrefix = rewriteDerivedPrefix; |
| this.emulatedInterfaces = emulateLibraryInterface; |
| this.retargetMethod = retargetMethod; |
| this.retargetMethodEmulatedDispatch = retargetMethodEmulatedDispatch; |
| this.legacyBackport = legacyBackport; |
| this.customConversions = customConversion; |
| this.dontRewriteInvocation = dontRewriteInvocation; |
| this.dontRetarget = dontRetarget; |
| this.wrapperConversions = wrapperConversion; |
| this.amendLibraryMethod = amendLibraryMethod; |
| } |
| |
| public static HumanRewritingFlags empty() { |
| return new HumanRewritingFlags( |
| ImmutableMap.of(), |
| ImmutableMap.of(), |
| ImmutableMap.of(), |
| ImmutableMap.of(), |
| ImmutableMap.of(), |
| ImmutableMap.of(), |
| ImmutableMap.of(), |
| ImmutableSet.of(), |
| ImmutableSet.of(), |
| ImmutableMap.of(), |
| ImmutableMap.of()); |
| } |
| |
| public static Builder builder(Reporter reporter, Origin origin) { |
| return new Builder(reporter, origin); |
| } |
| |
| public Builder newBuilder(Reporter reporter, Origin origin) { |
| return new Builder( |
| reporter, |
| origin, |
| rewritePrefix, |
| rewriteDerivedPrefix, |
| emulatedInterfaces, |
| retargetMethod, |
| retargetMethodEmulatedDispatch, |
| legacyBackport, |
| customConversions, |
| dontRewriteInvocation, |
| dontRetarget, |
| wrapperConversions, |
| amendLibraryMethod); |
| } |
| |
| public Map<String, String> getRewritePrefix() { |
| return rewritePrefix; |
| } |
| |
| public Map<String, Map<String, String>> getRewriteDerivedPrefix() { |
| return rewriteDerivedPrefix; |
| } |
| |
| public Map<DexType, DexType> getEmulatedInterfaces() { |
| return emulatedInterfaces; |
| } |
| |
| public Map<DexMethod, DexType> getRetargetMethod() { |
| return retargetMethod; |
| } |
| |
| public Map<DexMethod, DexType> getRetargetMethodEmulatedDispatch() { |
| return retargetMethodEmulatedDispatch; |
| } |
| |
| public Map<DexType, DexType> getLegacyBackport() { |
| return legacyBackport; |
| } |
| |
| public Map<DexType, DexType> getCustomConversions() { |
| return customConversions; |
| } |
| |
| public Set<DexMethod> getDontRewriteInvocation() { |
| return dontRewriteInvocation; |
| } |
| |
| public Set<DexType> getDontRetarget() { |
| return dontRetarget; |
| } |
| |
| public Map<DexType, Set<DexMethod>> getWrapperConversions() { |
| return wrapperConversions; |
| } |
| |
| public Map<DexMethod, MethodAccessFlags> getAmendLibraryMethod() { |
| return amendLibraryMethod; |
| } |
| |
| public boolean isEmpty() { |
| return rewritePrefix.isEmpty() |
| && rewriteDerivedPrefix.isEmpty() |
| && emulatedInterfaces.isEmpty() |
| && retargetMethod.isEmpty(); |
| } |
| |
| public static class Builder { |
| |
| private final Reporter reporter; |
| private final Origin origin; |
| |
| private final Map<String, String> rewritePrefix; |
| private final Map<String, Map<String, String>> rewriteDerivedPrefix; |
| private final Map<DexType, DexType> emulatedInterfaces; |
| private final Map<DexMethod, DexType> retargetMethod; |
| private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch; |
| private final Map<DexType, DexType> legacyBackport; |
| private final Map<DexType, DexType> customConversions; |
| private final Set<DexMethod> dontRewriteInvocation; |
| private final Set<DexType> dontRetarget; |
| private final Map<DexType, Set<DexMethod>> wrapperConversions; |
| private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod; |
| |
| Builder(Reporter reporter, Origin origin) { |
| this( |
| reporter, |
| origin, |
| new HashMap<>(), |
| new HashMap<>(), |
| new IdentityHashMap<>(), |
| new IdentityHashMap<>(), |
| new IdentityHashMap<>(), |
| new IdentityHashMap<>(), |
| new IdentityHashMap<>(), |
| Sets.newIdentityHashSet(), |
| Sets.newIdentityHashSet(), |
| new IdentityHashMap<>(), |
| new IdentityHashMap<>()); |
| } |
| |
| Builder( |
| Reporter reporter, |
| Origin origin, |
| Map<String, String> rewritePrefix, |
| Map<String, Map<String, String>> rewriteDerivedPrefix, |
| Map<DexType, DexType> emulateLibraryInterface, |
| Map<DexMethod, DexType> retargetMethod, |
| Map<DexMethod, DexType> retargetMethodEmulatedDispatch, |
| Map<DexType, DexType> backportCoreLibraryMember, |
| Map<DexType, DexType> customConversions, |
| Set<DexMethod> dontRewriteInvocation, |
| Set<DexType> dontRetargetLibMember, |
| Map<DexType, Set<DexMethod>> wrapperConversions, |
| Map<DexMethod, MethodAccessFlags> amendLibrary) { |
| this.reporter = reporter; |
| this.origin = origin; |
| this.rewritePrefix = new HashMap<>(rewritePrefix); |
| this.rewriteDerivedPrefix = new HashMap<>(rewriteDerivedPrefix); |
| this.emulatedInterfaces = new IdentityHashMap<>(emulateLibraryInterface); |
| this.retargetMethod = new IdentityHashMap<>(retargetMethod); |
| this.retargetMethodEmulatedDispatch = new IdentityHashMap<>(retargetMethodEmulatedDispatch); |
| this.legacyBackport = new IdentityHashMap<>(backportCoreLibraryMember); |
| this.customConversions = new IdentityHashMap<>(customConversions); |
| this.dontRewriteInvocation = Sets.newIdentityHashSet(); |
| this.dontRewriteInvocation.addAll(dontRewriteInvocation); |
| this.dontRetarget = Sets.newIdentityHashSet(); |
| this.dontRetarget.addAll(dontRetargetLibMember); |
| this.wrapperConversions = new IdentityHashMap<>(wrapperConversions); |
| this.amendLibraryMethod = new IdentityHashMap<>(amendLibrary); |
| } |
| |
| // Utility to set values. |
| private <K, V> void put(Map<K, V> map, K key, V value, String desc) { |
| if (map.containsKey(key) && !map.get(key).equals(value)) { |
| throw reporter.fatalError( |
| new StringDiagnostic( |
| "Invalid desugared library configuration. " |
| + " Duplicate assignment of key: '" |
| + key |
| + "' in sections for '" |
| + desc |
| + "'", |
| origin)); |
| } |
| map.put(key, value); |
| } |
| |
| public Builder putRewritePrefix(String prefix, String rewrittenPrefix) { |
| put( |
| rewritePrefix, |
| prefix, |
| rewrittenPrefix, |
| HumanDesugaredLibrarySpecificationParser.REWRITE_PREFIX_KEY); |
| return this; |
| } |
| |
| public Builder putRewriteDerivedPrefix( |
| String prefixToMatch, String prefixToRewrite, String rewrittenPrefix) { |
| Map<String, String> map = |
| rewriteDerivedPrefix.computeIfAbsent(prefixToMatch, k -> new HashMap<>()); |
| put( |
| map, |
| prefixToRewrite, |
| rewrittenPrefix, |
| HumanDesugaredLibrarySpecificationParser.REWRITE_DERIVED_PREFIX_KEY); |
| return this; |
| } |
| |
| public Builder putEmulatedInterface(DexType interfaceType, DexType rewrittenType) { |
| put( |
| emulatedInterfaces, |
| interfaceType, |
| rewrittenType, |
| HumanDesugaredLibrarySpecificationParser.EMULATE_INTERFACE_KEY); |
| return this; |
| } |
| |
| public Builder putCustomConversion(DexType dexType, DexType conversionType) { |
| put( |
| customConversions, |
| dexType, |
| conversionType, |
| HumanDesugaredLibrarySpecificationParser.CUSTOM_CONVERSION_KEY); |
| return this; |
| } |
| |
| public Builder addWrapperConversion(DexType dexType) { |
| return addWrapperConversion(dexType, Collections.emptySet()); |
| } |
| |
| public Builder addWrapperConversion(DexType dexType, Set<DexMethod> excludedMethods) { |
| wrapperConversions.put(dexType, excludedMethods); |
| return this; |
| } |
| |
| public Builder retargetMethod(DexMethod key, DexType rewrittenType) { |
| put( |
| retargetMethod, |
| key, |
| rewrittenType, |
| HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_KEY); |
| return this; |
| } |
| |
| public Builder retargetMethodEmulatedDispatch(DexMethod key, DexType rewrittenType) { |
| put( |
| retargetMethodEmulatedDispatch, |
| key, |
| rewrittenType, |
| HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_EMULATED_DISPATCH_KEY); |
| return this; |
| } |
| |
| public Builder putLegacyBackport(DexType backportType, DexType rewrittenBackportType) { |
| put( |
| legacyBackport, |
| backportType, |
| rewrittenBackportType, |
| HumanDesugaredLibrarySpecificationParser.BACKPORT_KEY); |
| return this; |
| } |
| |
| public Builder addDontRewriteInvocation(DexMethod dontRewrite) { |
| dontRewriteInvocation.add(dontRewrite); |
| return this; |
| } |
| |
| public Builder addDontRetargetLibMember(DexType dontRetargetLibMember) { |
| dontRetarget.add(dontRetargetLibMember); |
| return this; |
| } |
| |
| public Builder amendLibraryMethod(DexMethod member, MethodAccessFlags flags) { |
| amendLibraryMethod.put(member, flags); |
| return this; |
| } |
| |
| public HumanRewritingFlags build() { |
| validate(); |
| return new HumanRewritingFlags( |
| ImmutableMap.copyOf(rewritePrefix), |
| ImmutableMap.copyOf(rewriteDerivedPrefix), |
| ImmutableMap.copyOf(emulatedInterfaces), |
| ImmutableMap.copyOf(retargetMethod), |
| ImmutableMap.copyOf(retargetMethodEmulatedDispatch), |
| ImmutableMap.copyOf(legacyBackport), |
| ImmutableMap.copyOf(customConversions), |
| ImmutableSet.copyOf(dontRewriteInvocation), |
| ImmutableSet.copyOf(dontRetarget), |
| ImmutableMap.copyOf(wrapperConversions), |
| ImmutableMap.copyOf(amendLibraryMethod)); |
| } |
| |
| private void validate() { |
| SetView<DexType> dups = |
| Sets.intersection(customConversions.keySet(), wrapperConversions.keySet()); |
| if (!dups.isEmpty()) { |
| throw reporter.fatalError( |
| new StringDiagnostic( |
| "Invalid desugared library configuration. " |
| + "Duplicate types in custom conversions and wrapper conversions: " |
| + String.join( |
| ", ", dups.stream().map(DexType::toString).collect(Collectors.toSet())), |
| origin)); |
| } |
| } |
| } |
| } |