| // 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.experimental.startup; |
| |
| import com.android.tools.r8.experimental.startup.profile.StartupClass; |
| import com.android.tools.r8.experimental.startup.profile.StartupItem; |
| import com.android.tools.r8.experimental.startup.profile.StartupMethod; |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexProgramClass; |
| import com.android.tools.r8.graph.DexReference; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.GraphLens; |
| import com.android.tools.r8.graph.PrunedItems; |
| import com.android.tools.r8.synthesis.SyntheticItems; |
| import java.util.Collection; |
| import java.util.LinkedHashMap; |
| |
| public class NonEmptyStartupOrder extends StartupOrder { |
| |
| private final LinkedHashMap<DexReference, StartupItem> startupItems; |
| |
| NonEmptyStartupOrder(LinkedHashMap<DexReference, StartupItem> startupItems) { |
| assert !startupItems.isEmpty(); |
| this.startupItems = startupItems; |
| } |
| |
| @Override |
| public boolean contains(DexMethod method) { |
| return startupItems.containsKey(method); |
| } |
| |
| @Override |
| public boolean contains(DexType type) { |
| return startupItems.containsKey(type); |
| } |
| |
| @Override |
| public Collection<StartupItem> getItems() { |
| return startupItems.values(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return false; |
| } |
| |
| @Override |
| public StartupOrder rewrittenWithLens(GraphLens graphLens) { |
| LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems = |
| new LinkedHashMap<>(startupItems.size()); |
| for (StartupItem startupItem : startupItems.values()) { |
| // TODO(b/271822426): This should account for one-to-many mappings. e.g., when a bridge is |
| // created. |
| startupItem.apply( |
| startupClass -> |
| rewrittenStartupItems.put( |
| startupClass.getReference(), |
| StartupClass.builder() |
| .setClassReference(graphLens.lookupType(startupClass.getReference())) |
| .build()), |
| startupMethod -> |
| rewrittenStartupItems.put( |
| startupMethod.getReference(), |
| StartupMethod.builder() |
| .setMethodReference( |
| graphLens.getRenamedMethodSignature(startupMethod.getReference())) |
| .build())); |
| } |
| return createNonEmpty(rewrittenStartupItems); |
| } |
| |
| /** |
| * This is called to process the startup order before computing the startup layouts. |
| * |
| * <p>This processing makes two key changes to the startup order: |
| * |
| * <ul> |
| * <li>Synthetic startup classes on the form "SLcom/example/SyntheticContext;" represents that |
| * any method of any synthetic class that have been synthesized from SyntheticContext has |
| * been executed. This pass removes such entries from the startup order, and replaces them |
| * by all the methods from all of the synthetics that have been synthesized from |
| * SyntheticContext. |
| * <li>Moreover, this inserts a StartupClass event for all supertypes of a given class next to |
| * the class in the startup order. This ensures that the classes from the super hierarchy |
| * will be laid out close to their subclasses, at the point where the subclasses are used |
| * during startup. |
| * <p>Note that this normally follows from the trace already, except that the class |
| * initializers of interfaces are not executed when a subclass is used. |
| * </ul> |
| */ |
| @Override |
| public StartupOrder toStartupOrderForWriting(AppView<?> appView) { |
| LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems = |
| new LinkedHashMap<>(startupItems.size()); |
| for (StartupItem startupItem : startupItems.values()) { |
| addStartupItem(startupItem, rewrittenStartupItems, appView); |
| } |
| return createNonEmpty(rewrittenStartupItems); |
| } |
| |
| private static void addStartupItem( |
| StartupItem startupItem, |
| LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems, |
| AppView<?> appView) { |
| if (startupItem.isStartupClass()) { |
| addClassAndParentClasses( |
| startupItem.asStartupClass().getReference(), rewrittenStartupItems, appView); |
| } else { |
| rewrittenStartupItems.put(startupItem.getReference(), startupItem); |
| } |
| } |
| |
| private static boolean addClass( |
| DexProgramClass clazz, LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems) { |
| StartupItem previous = |
| rewrittenStartupItems.put( |
| clazz.getType(), StartupClass.builder().setClassReference(clazz.getType()).build()); |
| return previous == null; |
| } |
| |
| private static void addClassAndParentClasses( |
| DexType type, |
| LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems, |
| AppView<?> appView) { |
| DexProgramClass definition = appView.app().programDefinitionFor(type); |
| if (definition != null) { |
| addClassAndParentClasses(definition, rewrittenStartupItems, appView); |
| } |
| } |
| |
| private static void addClassAndParentClasses( |
| DexProgramClass clazz, |
| LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems, |
| AppView<?> appView) { |
| if (addClass(clazz, rewrittenStartupItems)) { |
| addParentClasses(clazz, rewrittenStartupItems, appView); |
| } |
| } |
| |
| private static void addParentClasses( |
| DexProgramClass clazz, |
| LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems, |
| AppView<?> appView) { |
| clazz.forEachImmediateSupertype( |
| supertype -> addClassAndParentClasses(supertype, rewrittenStartupItems, appView)); |
| } |
| |
| @Override |
| public StartupOrder withoutPrunedItems(PrunedItems prunedItems, SyntheticItems syntheticItems) { |
| LinkedHashMap<DexReference, StartupItem> rewrittenStartupItems = |
| new LinkedHashMap<>(startupItems.size()); |
| for (StartupItem startupItem : startupItems.values()) { |
| // Only prune non-synthetic classes, since the pruning of a class does not imply that all |
| // classes synthesized from it have been pruned. |
| startupItem.accept( |
| startupClass -> { |
| if (!prunedItems.isRemoved(startupClass.getReference())) { |
| rewrittenStartupItems.put(startupClass.getReference(), startupItem); |
| } |
| }, |
| startupMethod -> { |
| if (!prunedItems.isRemoved(startupMethod.getReference())) { |
| rewrittenStartupItems.put(startupMethod.getReference(), startupItem); |
| } |
| }); |
| } |
| return createNonEmpty(rewrittenStartupItems); |
| } |
| |
| private StartupOrder createNonEmpty(LinkedHashMap<DexReference, StartupItem> startupItems) { |
| if (startupItems.isEmpty()) { |
| assert false; |
| return empty(); |
| } |
| return new NonEmptyStartupOrder(startupItems); |
| } |
| } |