| // Copyright (c) 2022, 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.GlobalSyntheticsResourceProvider; |
| import com.android.tools.r8.ProgramResource; |
| import com.android.tools.r8.ProgramResource.Kind; |
| import com.android.tools.r8.ProgramResourceProvider; |
| import com.android.tools.r8.ResourceException; |
| import com.android.tools.r8.Version; |
| import com.android.tools.r8.origin.ArchiveEntryOrigin; |
| import com.google.common.io.ByteStreams; |
| import java.io.IOException; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.function.Function; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipInputStream; |
| |
| public class InternalGlobalSyntheticsProgramProvider implements ProgramResourceProvider { |
| |
| private final List<GlobalSyntheticsResourceProvider> providers; |
| private List<ProgramResource> resources = null; |
| |
| public InternalGlobalSyntheticsProgramProvider(List<GlobalSyntheticsResourceProvider> providers) { |
| this.providers = providers; |
| } |
| |
| @Override |
| public Collection<ProgramResource> getProgramResources() throws ResourceException { |
| if (resources == null) { |
| ensureResources(); |
| } |
| return resources; |
| } |
| |
| private synchronized void ensureResources() throws ResourceException { |
| if (resources != null) { |
| return; |
| } |
| List<ProgramResource> resources = new ArrayList<>(); |
| Set<String> seen = new HashSet<>(); |
| for (GlobalSyntheticsResourceProvider provider : providers) { |
| List<Function<Kind, ProgramResource>> delayedResouces = new ArrayList<>(); |
| Kind providerKind = null; |
| try (ZipInputStream stream = new ZipInputStream(provider.getByteStream())) { |
| ZipEntry entry; |
| while (null != (entry = stream.getNextEntry())) { |
| String name = entry.getName(); |
| if (name.equals(InternalGlobalSyntheticsProgramConsumer.OUTPUT_KIND_ENTRY_NAME)) { |
| providerKind = |
| Kind.valueOf(new String(ByteStreams.toByteArray(stream), StandardCharsets.UTF_8)); |
| } else if (name.equals( |
| InternalGlobalSyntheticsProgramConsumer.COMPILER_INFO_ENTRY_NAME)) { |
| String version = new String(ByteStreams.toByteArray(stream), StandardCharsets.UTF_8); |
| if (!Version.getVersionString().equals(version)) { |
| throw new ResourceException( |
| provider.getOrigin(), |
| "Outdated or inconsistent global synthetics information." |
| + "\nGlobal synthetics information version: " |
| + version |
| + "\nCompiler version: " |
| + Version.getVersionString()); |
| } |
| } else if (name.endsWith(FileUtils.GLOBAL_SYNTHETIC_EXTENSION) && seen.add(name)) { |
| ArchiveEntryOrigin origin = new ArchiveEntryOrigin(name, provider.getOrigin()); |
| String descriptor = guessTypeDescriptor(name); |
| byte[] bytes = ByteStreams.toByteArray(stream); |
| Set<String> descriptors = Collections.singleton(descriptor); |
| delayedResouces.add( |
| kind -> OneShotByteResource.create(kind, origin, bytes, descriptors)); |
| } |
| } |
| } catch (IOException e) { |
| throw new ResourceException(provider.getOrigin(), e); |
| } |
| if (providerKind == null) { |
| throw new ResourceException( |
| provider.getOrigin(), |
| "Invalid global synthetics provider does not specify its content kind."); |
| } |
| for (Function<Kind, ProgramResource> fn : delayedResouces) { |
| resources.add(fn.apply(providerKind)); |
| } |
| } |
| this.resources = resources; |
| } |
| |
| private String guessTypeDescriptor(String name) { |
| String noExt = name.substring(0, name.length() - FileUtils.GLOBAL_SYNTHETIC_EXTENSION.length()); |
| String classExt = noExt + FileUtils.CLASS_EXTENSION; |
| return DescriptorUtils.guessTypeDescriptor(classExt); |
| } |
| } |