| // 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.ClassFileResourceProvider; |
| import com.android.tools.r8.ProgramResource; |
| import com.android.tools.r8.ResourceException; |
| import com.android.tools.r8.errors.CompilationError; |
| import com.android.tools.r8.graph.ClassKind; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.JarApplicationReader; |
| import com.android.tools.r8.graph.JarClassFileReader; |
| import com.google.common.collect.ImmutableListMultimap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Multimap; |
| import com.google.common.collect.Sets; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| |
| /** Represents a provider for classes loaded from different sources. */ |
| public abstract class ClassProvider<T extends DexClass> { |
| private final ClassKind<T> classKind; |
| |
| ClassProvider(ClassKind<T> classKind) { |
| this.classKind = classKind; |
| } |
| |
| /** The kind of the classes created by the provider. */ |
| final ClassKind<T> getClassKind() { |
| return classKind; |
| } |
| |
| /** |
| * The provider uses the callback to return all the classes that might |
| * be associated with the descriptor asked for. |
| * |
| * NOTE: the provider is not required to cache created classes and this |
| * method may create a new class instance in case it is called twice for |
| * the same type. For this reason it is recommended that the provider |
| * user only calls this method once per any given type. |
| * |
| * NOTE: thread-safe. |
| */ |
| public abstract void collectClass(DexType type, Consumer<T> classConsumer); |
| |
| /** |
| * Returns all the types of classes that might be produced by this provider. |
| * |
| * NOTE: thread-safe. |
| */ |
| public abstract Collection<DexType> collectTypes(); |
| |
| /** Create class provider for java class resource provider. */ |
| public static <T extends DexClass> ClassProvider<T> forClassFileResources( |
| ClassKind<T> classKind, ClassFileResourceProvider provider, JarApplicationReader reader) { |
| return new ClassFileResourceReader<>(classKind, provider, reader); |
| } |
| |
| /** Create class provider for preloaded classes, classes may have conflicting names. */ |
| public static <T extends DexClass> ClassProvider<T> forPreloadedClasses( |
| ClassKind<T> classKind, Collection<T> classes) { |
| ImmutableListMultimap.Builder<DexType, T> builder = ImmutableListMultimap.builder(); |
| for (T clazz : classes) { |
| builder.put(clazz.type, clazz); |
| } |
| return new PreloadedClassProvider<>(classKind, builder.build()); |
| } |
| |
| public FilteringClassProvider<T> without(Set<DexType> filteredTypes) { |
| return new FilteringClassProvider<>(classKind, this, filteredTypes); |
| } |
| |
| /** Create class provider for preloaded classes. */ |
| public static <T extends DexClass> ClassProvider<T> combine( |
| ClassKind<T> classKind, List<ClassProvider<T>> providers) { |
| return new CombinedClassProvider<>(classKind, providers); |
| } |
| |
| private static class ClassFileResourceReader<T extends DexClass> extends ClassProvider<T> { |
| private final ClassKind<T> classKind; |
| private final ClassFileResourceProvider provider; |
| private final JarApplicationReader reader; |
| |
| private ClassFileResourceReader( |
| ClassKind<T> classKind, ClassFileResourceProvider provider, JarApplicationReader reader) { |
| super(classKind); |
| this.classKind = classKind; |
| this.provider = provider; |
| this.reader = reader; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public void collectClass(DexType type, Consumer<T> classConsumer) { |
| if (provider instanceof InternalClasspathOrLibraryClassProvider) { |
| InternalClasspathOrLibraryClassProvider<T> internalProvider = |
| (InternalClasspathOrLibraryClassProvider<T>) provider; |
| T clazz = internalProvider.getClass(type); |
| if (clazz != null) { |
| classConsumer.accept(clazz); |
| } |
| return; |
| } |
| String descriptor = type.descriptor.toString(); |
| ProgramResource resource = provider.getProgramResource(descriptor); |
| if (resource != null) { |
| try { |
| JarClassFileReader<T> classReader = |
| new JarClassFileReader<>(reader, classConsumer, classKind); |
| classReader.read(resource); |
| } catch (ResourceException e) { |
| throw new CompilationError("Failed to load class: " + descriptor, e); |
| } |
| } |
| } |
| |
| @Override |
| public Collection<DexType> collectTypes() { |
| if (provider instanceof InternalClasspathOrLibraryClassProvider) { |
| InternalClasspathOrLibraryClassProvider<?> internalProvider = |
| (InternalClasspathOrLibraryClassProvider<?>) provider; |
| return internalProvider.getTypes(); |
| } |
| List<DexType> types = new ArrayList<>(); |
| for (String descriptor : provider.getClassDescriptors()) { |
| types.add(reader.options.itemFactory.createType(descriptor)); |
| } |
| return types; |
| } |
| |
| @Override |
| public String toString() { |
| return "class-resource-provider(" + provider.toString() + ")"; |
| } |
| } |
| |
| private static class PreloadedClassProvider<T extends DexClass> extends ClassProvider<T> { |
| private final Multimap<DexType, T> classes; |
| |
| private PreloadedClassProvider(ClassKind<T> classKind, Multimap<DexType, T> classes) { |
| super(classKind); |
| this.classes = classes; |
| } |
| |
| @Override |
| public void collectClass(DexType type, Consumer<T> classConsumer) { |
| for (T clazz : classes.get(type)) { |
| classConsumer.accept(clazz); |
| } |
| } |
| |
| @Override |
| public Collection<DexType> collectTypes() { |
| return classes.keys(); |
| } |
| |
| @Override |
| public String toString() { |
| return "preloaded(" + classes.size() + ")"; |
| } |
| } |
| |
| /** Class provider which ignores a list of filtered classes */ |
| private static class FilteringClassProvider<T extends DexClass> extends ClassProvider<T> { |
| private final ClassProvider<T> provider; |
| private final Set<DexType> filteredOut; |
| |
| FilteringClassProvider( |
| ClassKind<T> classKind, ClassProvider<T> provider, Set<DexType> filteredOut) { |
| super(classKind); |
| assert !(provider instanceof FilteringClassProvider) : "Nested Filtering class providers"; |
| this.provider = provider; |
| this.filteredOut = filteredOut; |
| } |
| |
| @Override |
| public FilteringClassProvider<T> without(Set<DexType> filteredTypes) { |
| ImmutableSet<DexType> newSet = |
| ImmutableSet.<DexType>builder().addAll(filteredOut).addAll(filteredTypes).build(); |
| return new FilteringClassProvider<>(getClassKind(), provider, newSet); |
| } |
| |
| @Override |
| public void collectClass(DexType type, Consumer<T> classConsumer) { |
| if (filteredOut.contains(type)) { |
| return; |
| } |
| provider.collectClass(type, classConsumer); |
| } |
| |
| @Override |
| public Collection<DexType> collectTypes() { |
| Collection<DexType> dexTypes = provider.collectTypes(); |
| dexTypes.removeAll(filteredOut); |
| return dexTypes; |
| } |
| |
| @Override |
| public String toString() { |
| return provider + " without " + filteredOut; |
| } |
| } |
| |
| private static class CombinedClassProvider<T extends DexClass> extends ClassProvider<T> { |
| private final List<ClassProvider<T>> providers; |
| |
| private CombinedClassProvider(ClassKind<T> classKind, List<ClassProvider<T>> providers) { |
| super(classKind); |
| this.providers = providers; |
| } |
| |
| @Override |
| public void collectClass(DexType type, Consumer<T> classConsumer) { |
| for (ClassProvider<T> provider : providers) { |
| provider.collectClass(type, classConsumer); |
| } |
| } |
| |
| @Override |
| public Collection<DexType> collectTypes() { |
| Set<DexType> types = Sets.newIdentityHashSet(); |
| for (ClassProvider<T> provider : providers) { |
| types.addAll(provider.collectTypes()); |
| } |
| return types; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(); |
| String prefix = "combined("; |
| for (ClassProvider<T> provider : providers) { |
| builder.append(prefix); |
| prefix = ", "; |
| builder.append(provider); |
| } |
| return builder.append(")").toString(); |
| } |
| } |
| } |