|  | // 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; | 
|  |  | 
|  | import com.android.tools.r8.cf.code.CfInstruction; | 
|  | import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext; | 
|  | import com.android.tools.r8.graph.DexItemFactory; | 
|  | import com.android.tools.r8.graph.ProgramMethod; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Collection; | 
|  | import java.util.List; | 
|  |  | 
|  | /** | 
|  | * A description of the desugaring task. | 
|  | * | 
|  | * <p>The description encodes if there are any side effects that must be done as part of the | 
|  | * desugaring checks (such as issue diagnostics) as well as if a given instruction needs to be | 
|  | * desugared, and if so, how to desugar it. The process of computing a description should be | 
|  | * side-effect free. All side effects should be done either in the 'scan' or the | 
|  | * 'desugarInstruction' callbacks. | 
|  | */ | 
|  | public class DesugarDescription { | 
|  |  | 
|  | private static final DesugarDescription NOTHING = new DesugarDescription(); | 
|  |  | 
|  | private DesugarDescription() {} | 
|  |  | 
|  | public void scan() {} | 
|  |  | 
|  | public boolean needsDesugaring() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public Collection<CfInstruction> desugarInstruction( | 
|  | FreshLocalProvider freshLocalProvider, | 
|  | LocalStackAllocator localStackAllocator, | 
|  | CfInstructionDesugaringEventConsumer eventConsumer, | 
|  | ProgramMethod context, | 
|  | MethodProcessingContext methodProcessingContext, | 
|  | DexItemFactory dexItemFactory) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public static DesugarDescription nothing() { | 
|  | assert NOTHING == builder().build(); | 
|  | return NOTHING; | 
|  | } | 
|  |  | 
|  | public static Builder builder() { | 
|  | return InitialBuilder.getInstance(); | 
|  | } | 
|  |  | 
|  | @FunctionalInterface | 
|  | public interface ScanCallback { | 
|  | void scan(); | 
|  | } | 
|  |  | 
|  | @FunctionalInterface | 
|  | public interface DesugarCallback { | 
|  | Collection<CfInstruction> desugarInstruction( | 
|  | FreshLocalProvider freshLocalProvider, | 
|  | LocalStackAllocator localStackAllocator, | 
|  | CfInstructionDesugaringEventConsumer eventConsumer, | 
|  | ProgramMethod context, | 
|  | MethodProcessingContext methodProcessingContext, | 
|  | DexItemFactory dexItemFactory); | 
|  | } | 
|  |  | 
|  | public abstract static class Builder { | 
|  | public abstract DesugarDescription build(); | 
|  |  | 
|  | public abstract Builder addScanEffect(ScanCallback callback); | 
|  |  | 
|  | public abstract Builder setDesugarRewrite(DesugarCallback callback); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Initial builder is an empty singleton. Any actual change will result in the allocation of a | 
|  | * non-empty builder. This ensures that the trivial case has zero allocation overhead. | 
|  | */ | 
|  | static class InitialBuilder extends Builder { | 
|  | static final InitialBuilder INSTANCE = new InitialBuilder(); | 
|  |  | 
|  | public static InitialBuilder getInstance() { | 
|  | return INSTANCE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DesugarDescription build() { | 
|  | return NOTHING; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Builder addScanEffect(ScanCallback callback) { | 
|  | return new NonEmptyBuilder().addScanEffect(callback); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Builder setDesugarRewrite(DesugarCallback callback) { | 
|  | return new NonEmptyBuilder().setDesugarRewrite(callback); | 
|  | } | 
|  | } | 
|  |  | 
|  | static class NonEmptyBuilder extends Builder { | 
|  |  | 
|  | List<ScanCallback> scanEffects = new ArrayList<>(); | 
|  | DesugarCallback desugarRewrite = null; | 
|  |  | 
|  | @Override | 
|  | public Builder addScanEffect(ScanCallback callback) { | 
|  | assert callback != null; | 
|  | scanEffects.add(callback); | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Builder setDesugarRewrite(DesugarCallback desugarRewrite) { | 
|  | assert this.desugarRewrite == null; | 
|  | assert desugarRewrite != null; | 
|  | this.desugarRewrite = desugarRewrite; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DesugarDescription build() { | 
|  | return new DesugarDescription() { | 
|  | @Override | 
|  | public void scan() { | 
|  | scanEffects.forEach(ScanCallback::scan); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean needsDesugaring() { | 
|  | return desugarRewrite != null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Collection<CfInstruction> desugarInstruction( | 
|  | FreshLocalProvider freshLocalProvider, | 
|  | LocalStackAllocator localStackAllocator, | 
|  | CfInstructionDesugaringEventConsumer eventConsumer, | 
|  | ProgramMethod context, | 
|  | MethodProcessingContext methodProcessingContext, | 
|  | DexItemFactory dexItemFactory) { | 
|  | return desugarRewrite == null | 
|  | ? null | 
|  | : desugarRewrite.desugarInstruction( | 
|  | freshLocalProvider, | 
|  | localStackAllocator, | 
|  | eventConsumer, | 
|  | context, | 
|  | methodProcessingContext, | 
|  | dexItemFactory); | 
|  | } | 
|  | }; | 
|  | } | 
|  | } | 
|  | } |