|  | // Copyright (c) 2020, 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.shaking; | 
|  |  | 
|  | import com.android.tools.r8.graph.AppView; | 
|  | import com.android.tools.r8.graph.DexReference; | 
|  | import com.android.tools.r8.graph.EnclosingMethodAttribute; | 
|  | import com.android.tools.r8.graph.ProgramDefinition; | 
|  | import com.android.tools.r8.shaking.KeepInfo.Builder; | 
|  | import com.android.tools.r8.shaking.KeepReason.ReflectiveUseFrom; | 
|  | import com.android.tools.r8.utils.InternalOptions; | 
|  | import com.google.common.collect.Sets; | 
|  | import java.util.HashSet; | 
|  | import java.util.Set; | 
|  | import java.util.function.Consumer; | 
|  |  | 
|  | /** Keep information that can be associated with any item, i.e., class, method or field. */ | 
|  | public abstract class KeepInfo<B extends Builder<B, K>, K extends KeepInfo<B, K>> { | 
|  |  | 
|  | private final boolean allowAccessModification; | 
|  | private final boolean allowAnnotationRemoval; | 
|  | private final boolean allowMinification; | 
|  | private final boolean allowOptimization; | 
|  | private final boolean allowRepackaging; | 
|  | private final boolean allowShrinking; | 
|  | private final boolean allowSignatureRemoval; | 
|  | private final boolean checkDiscarded; | 
|  | private final boolean requireAccessModificationForRepackaging; | 
|  |  | 
|  | private KeepInfo( | 
|  | boolean allowAccessModification, | 
|  | boolean allowAnnotationRemoval, | 
|  | boolean allowMinification, | 
|  | boolean allowOptimization, | 
|  | boolean allowRepackaging, | 
|  | boolean allowShrinking, | 
|  | boolean allowSignatureRemoval, | 
|  | boolean checkDiscarded, | 
|  | boolean requireAccessModificationForRepackaging) { | 
|  | this.allowAccessModification = allowAccessModification; | 
|  | this.allowAnnotationRemoval = allowAnnotationRemoval; | 
|  | this.allowMinification = allowMinification; | 
|  | this.allowOptimization = allowOptimization; | 
|  | this.allowRepackaging = allowRepackaging; | 
|  | this.allowShrinking = allowShrinking; | 
|  | this.allowSignatureRemoval = allowSignatureRemoval; | 
|  | this.checkDiscarded = checkDiscarded; | 
|  | this.requireAccessModificationForRepackaging = requireAccessModificationForRepackaging; | 
|  | } | 
|  |  | 
|  | KeepInfo(B builder) { | 
|  | this( | 
|  | builder.isAccessModificationAllowed(), | 
|  | builder.isAnnotationRemovalAllowed(), | 
|  | builder.isMinificationAllowed(), | 
|  | builder.isOptimizationAllowed(), | 
|  | builder.isRepackagingAllowed(), | 
|  | builder.isShrinkingAllowed(), | 
|  | builder.isSignatureRemovalAllowed(), | 
|  | builder.isCheckDiscardedEnabled(), | 
|  | builder.isAccessModificationRequiredForRepackaging()); | 
|  | } | 
|  |  | 
|  | public static Joiner<?, ?, ?> newEmptyJoinerFor(DexReference reference) { | 
|  | return reference.apply( | 
|  | clazz -> KeepClassInfo.newEmptyJoiner(), | 
|  | field -> KeepFieldInfo.newEmptyJoiner(), | 
|  | method -> KeepMethodInfo.newEmptyJoiner()); | 
|  | } | 
|  |  | 
|  | abstract B builder(); | 
|  |  | 
|  | /** | 
|  | * True if an item may have all of its annotations removed. | 
|  | * | 
|  | * <p>If this returns false, some annotations may still be removed if the configuration does not | 
|  | * keep all annotation attributes. | 
|  | */ | 
|  | public boolean isAnnotationRemovalAllowed(GlobalKeepInfoConfiguration configuration) { | 
|  | return configuration.isAnnotationRemovalEnabled() && internalIsAnnotationRemovalAllowed(); | 
|  | } | 
|  |  | 
|  | boolean internalIsAnnotationRemovalAllowed() { | 
|  | return allowAnnotationRemoval; | 
|  | } | 
|  |  | 
|  | public boolean isCheckDiscardedEnabled(GlobalKeepInfoConfiguration configuration) { | 
|  | return internalIsCheckDiscardedEnabled(); | 
|  | } | 
|  |  | 
|  | boolean internalIsCheckDiscardedEnabled() { | 
|  | return checkDiscarded; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * True if an item must be present in the output. | 
|  | * | 
|  | * @deprecated Prefer task dependent predicates. | 
|  | */ | 
|  | @Deprecated | 
|  | public boolean isPinned(GlobalKeepInfoConfiguration configuration) { | 
|  | return !isOptimizationAllowed(configuration) || !isShrinkingAllowed(configuration); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * True if an item may have its name minified/changed. | 
|  | * | 
|  | * <p>This method requires knowledge of the global configuration as that can override the concrete | 
|  | * value on a given item. | 
|  | */ | 
|  | public boolean isMinificationAllowed(GlobalKeepInfoConfiguration configuration) { | 
|  | return configuration.isMinificationEnabled() && internalIsMinificationAllowed(); | 
|  | } | 
|  |  | 
|  | boolean internalIsMinificationAllowed() { | 
|  | return allowMinification; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * True if an item may be optimized (i.e., the item is not soft pinned). | 
|  | * | 
|  | * <p>This method requires knowledge of the global configuration as that can override the concrete | 
|  | * value on a given item. | 
|  | */ | 
|  | public boolean isOptimizationAllowed(GlobalKeepInfoConfiguration configuration) { | 
|  | return configuration.isOptimizationEnabled() && internalIsOptimizationAllowed(); | 
|  | } | 
|  |  | 
|  | boolean internalIsOptimizationAllowed() { | 
|  | return allowOptimization; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * True if an item is subject to shrinking (i.e., tree shaking). | 
|  | * | 
|  | * <p>This method requires knowledge of the global configuration as that can override the concrete | 
|  | * value on a given item. | 
|  | */ | 
|  | public boolean isShrinkingAllowed(GlobalKeepInfoConfiguration configuration) { | 
|  | return configuration.isTreeShakingEnabled() && internalIsShrinkingAllowed(); | 
|  | } | 
|  |  | 
|  | boolean internalIsShrinkingAllowed() { | 
|  | return allowShrinking; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * True if an item may have its generic signature removed. | 
|  | * | 
|  | * <p>This method requires knowledge of the global configuration as that can override the concrete | 
|  | * value on a given item. | 
|  | */ | 
|  | public boolean isSignatureRemovalAllowed(GlobalKeepInfoConfiguration configuration) { | 
|  | if (!configuration.isKeepAttributesSignatureEnabled()) { | 
|  | return true; | 
|  | } | 
|  | return !configuration.isForceProguardCompatibilityEnabled() | 
|  | && internalIsSignatureRemovalAllowed(); | 
|  | } | 
|  |  | 
|  | boolean internalIsSignatureRemovalAllowed() { | 
|  | return allowSignatureRemoval; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * True if an item may be repackaged. | 
|  | * | 
|  | * <p>This method requires knowledge of the global configuration as that can override the concrete | 
|  | * value on a given item. | 
|  | */ | 
|  | public abstract boolean isRepackagingAllowed( | 
|  | ProgramDefinition definition, GlobalKeepInfoConfiguration configuration); | 
|  |  | 
|  | boolean internalIsRepackagingAllowed() { | 
|  | return allowRepackaging; | 
|  | } | 
|  |  | 
|  | boolean internalIsAccessModificationRequiredForRepackaging() { | 
|  | return requireAccessModificationForRepackaging; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * True if an item may have its access flags modified. | 
|  | * | 
|  | * <p>This method requires knowledge of the global access modification as that will override the | 
|  | * concrete value on a given item. | 
|  | * | 
|  | * @param configuration Global configuration object to determine access modification. | 
|  | */ | 
|  | public boolean isAccessModificationAllowed(GlobalKeepInfoConfiguration configuration) { | 
|  | return configuration.isAccessModificationEnabled() && internalIsAccessModificationAllowed(); | 
|  | } | 
|  |  | 
|  | // Internal accessor for the items access-modification bit. | 
|  | boolean internalIsAccessModificationAllowed() { | 
|  | return allowAccessModification; | 
|  | } | 
|  |  | 
|  | public boolean isEnclosingMethodAttributeRemovalAllowed( | 
|  | GlobalKeepInfoConfiguration configuration, | 
|  | EnclosingMethodAttribute enclosingMethodAttribute, | 
|  | AppView<AppInfoWithLiveness> appView) { | 
|  | if (!configuration.isKeepEnclosingMethodAttributeEnabled()) { | 
|  | return true; | 
|  | } | 
|  | if (configuration.isForceProguardCompatibilityEnabled()) { | 
|  | return false; | 
|  | } | 
|  | return !isPinned(configuration) || !enclosingMethodAttribute.isEnclosingPinned(appView); | 
|  | } | 
|  |  | 
|  | public boolean isInnerClassesAttributeRemovalAllowed(GlobalKeepInfoConfiguration configuration) { | 
|  | if (!configuration.isKeepInnerClassesAttributeEnabled()) { | 
|  | return true; | 
|  | } | 
|  | return !(configuration.isForceProguardCompatibilityEnabled() || isPinned(configuration)); | 
|  | } | 
|  |  | 
|  | public boolean isInnerClassesAttributeRemovalAllowed( | 
|  | GlobalKeepInfoConfiguration configuration, | 
|  | EnclosingMethodAttribute enclosingMethodAttribute) { | 
|  | if (!configuration.isKeepInnerClassesAttributeEnabled()) { | 
|  | return true; | 
|  | } | 
|  | if (configuration.isForceProguardCompatibilityEnabled()) { | 
|  | return false; | 
|  | } | 
|  | // The inner class is dependent on the enclosingMethodAttribute and since it has been pruned | 
|  | // we can also remove this inner class relationship. | 
|  | return enclosingMethodAttribute == null || !isPinned(configuration); | 
|  | } | 
|  |  | 
|  | public abstract boolean isTop(); | 
|  |  | 
|  | public abstract boolean isBottom(); | 
|  |  | 
|  | public boolean isLessThanOrEquals(K other) { | 
|  | // An item is less, aka, lower in the lattice, if each of its attributes is at least as | 
|  | // permissive of that on other. | 
|  | return (allowAccessModification || !other.internalIsAccessModificationAllowed()) | 
|  | && (allowAnnotationRemoval || !other.internalIsAnnotationRemovalAllowed()) | 
|  | && (allowMinification || !other.internalIsMinificationAllowed()) | 
|  | && (allowOptimization || !other.internalIsOptimizationAllowed()) | 
|  | && (allowRepackaging || !other.internalIsRepackagingAllowed()) | 
|  | && (allowShrinking || !other.internalIsShrinkingAllowed()) | 
|  | && (allowSignatureRemoval || !other.internalIsSignatureRemovalAllowed()) | 
|  | && (!checkDiscarded || other.internalIsCheckDiscardedEnabled()); | 
|  | } | 
|  |  | 
|  | /** Builder to construct an arbitrary keep info object. */ | 
|  | public abstract static class Builder<B extends Builder<B, K>, K extends KeepInfo<B, K>> { | 
|  |  | 
|  | abstract B self(); | 
|  |  | 
|  | abstract K doBuild(); | 
|  |  | 
|  | abstract K getTopInfo(); | 
|  |  | 
|  | abstract K getBottomInfo(); | 
|  |  | 
|  | abstract boolean isEqualTo(K other); | 
|  |  | 
|  | protected K original; | 
|  | private boolean allowAccessModification; | 
|  | private boolean allowAnnotationRemoval; | 
|  | private boolean allowMinification; | 
|  | private boolean allowRepackaging; | 
|  | private boolean allowOptimization; | 
|  | private boolean allowShrinking; | 
|  | private boolean allowSignatureRemoval; | 
|  | private boolean checkDiscarded; | 
|  | private boolean requireAccessModificationForRepackaging; | 
|  |  | 
|  | Builder() { | 
|  | // Default initialized. Use should be followed by makeTop/makeBottom. | 
|  | } | 
|  |  | 
|  | Builder(K original) { | 
|  | this.original = original; | 
|  | allowAccessModification = original.internalIsAccessModificationAllowed(); | 
|  | allowAnnotationRemoval = original.internalIsAnnotationRemovalAllowed(); | 
|  | allowMinification = original.internalIsMinificationAllowed(); | 
|  | allowOptimization = original.internalIsOptimizationAllowed(); | 
|  | allowRepackaging = original.internalIsRepackagingAllowed(); | 
|  | allowShrinking = original.internalIsShrinkingAllowed(); | 
|  | allowSignatureRemoval = original.internalIsSignatureRemovalAllowed(); | 
|  | checkDiscarded = original.internalIsCheckDiscardedEnabled(); | 
|  | requireAccessModificationForRepackaging = | 
|  | original.internalIsAccessModificationRequiredForRepackaging(); | 
|  | } | 
|  |  | 
|  | B makeTop() { | 
|  | disallowAccessModification(); | 
|  | disallowAnnotationRemoval(); | 
|  | disallowMinification(); | 
|  | disallowOptimization(); | 
|  | disallowRepackaging(); | 
|  | disallowShrinking(); | 
|  | disallowSignatureRemoval(); | 
|  | unsetCheckDiscarded(); | 
|  | requireAccessModificationForRepackaging(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | B makeBottom() { | 
|  | allowAccessModification(); | 
|  | allowAnnotationRemoval(); | 
|  | allowMinification(); | 
|  | allowOptimization(); | 
|  | allowRepackaging(); | 
|  | allowShrinking(); | 
|  | allowSignatureRemoval(); | 
|  | unsetCheckDiscarded(); | 
|  | unsetRequireAccessModificationForRepackaging(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public K build() { | 
|  | if (original != null) { | 
|  | if (internalIsEqualTo(original)) { | 
|  | return original; | 
|  | } | 
|  | if (internalIsEqualTo(getTopInfo())) { | 
|  | return getTopInfo(); | 
|  | } | 
|  | if (internalIsEqualTo(getBottomInfo())) { | 
|  | return getBottomInfo(); | 
|  | } | 
|  | } | 
|  | return doBuild(); | 
|  | } | 
|  |  | 
|  | boolean internalIsEqualTo(K other) { | 
|  | return isAccessModificationAllowed() == other.internalIsAccessModificationAllowed() | 
|  | && isAnnotationRemovalAllowed() == other.internalIsAnnotationRemovalAllowed() | 
|  | && isMinificationAllowed() == other.internalIsMinificationAllowed() | 
|  | && isOptimizationAllowed() == other.internalIsOptimizationAllowed() | 
|  | && isRepackagingAllowed() == other.internalIsRepackagingAllowed() | 
|  | && isShrinkingAllowed() == other.internalIsShrinkingAllowed() | 
|  | && isSignatureRemovalAllowed() == other.internalIsSignatureRemovalAllowed() | 
|  | && isCheckDiscardedEnabled() == other.internalIsCheckDiscardedEnabled() | 
|  | && isAccessModificationRequiredForRepackaging() | 
|  | == other.internalIsAccessModificationRequiredForRepackaging(); | 
|  | } | 
|  |  | 
|  | public boolean isAccessModificationRequiredForRepackaging() { | 
|  | return requireAccessModificationForRepackaging; | 
|  | } | 
|  |  | 
|  | public boolean isAccessModificationAllowed() { | 
|  | return allowAccessModification; | 
|  | } | 
|  |  | 
|  | public boolean isAnnotationRemovalAllowed() { | 
|  | return allowAnnotationRemoval; | 
|  | } | 
|  |  | 
|  | public boolean isCheckDiscardedEnabled() { | 
|  | return checkDiscarded; | 
|  | } | 
|  |  | 
|  | public boolean isMinificationAllowed() { | 
|  | return allowMinification; | 
|  | } | 
|  |  | 
|  | public boolean isOptimizationAllowed() { | 
|  | return allowOptimization; | 
|  | } | 
|  |  | 
|  | public boolean isRepackagingAllowed() { | 
|  | return allowRepackaging; | 
|  | } | 
|  |  | 
|  | public boolean isShrinkingAllowed() { | 
|  | return allowShrinking; | 
|  | } | 
|  |  | 
|  | public boolean isSignatureRemovalAllowed() { | 
|  | return allowSignatureRemoval; | 
|  | } | 
|  |  | 
|  | public B setAllowMinification(boolean allowMinification) { | 
|  | this.allowMinification = allowMinification; | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public B allowMinification() { | 
|  | return setAllowMinification(true); | 
|  | } | 
|  |  | 
|  | public B disallowMinification() { | 
|  | return setAllowMinification(false); | 
|  | } | 
|  |  | 
|  | public B setAllowRepackaging(boolean allowRepackaging) { | 
|  | this.allowRepackaging = allowRepackaging; | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public B allowRepackaging() { | 
|  | return setAllowRepackaging(true); | 
|  | } | 
|  |  | 
|  | public B disallowRepackaging() { | 
|  | return setAllowRepackaging(false); | 
|  | } | 
|  |  | 
|  | public B setAllowOptimization(boolean allowOptimization) { | 
|  | this.allowOptimization = allowOptimization; | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public B allowOptimization() { | 
|  | return setAllowOptimization(true); | 
|  | } | 
|  |  | 
|  | public B disallowOptimization() { | 
|  | return setAllowOptimization(false); | 
|  | } | 
|  |  | 
|  | public B setAllowShrinking(boolean allowShrinking) { | 
|  | this.allowShrinking = allowShrinking; | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public B allowShrinking() { | 
|  | return setAllowShrinking(true); | 
|  | } | 
|  |  | 
|  | public B disallowShrinking() { | 
|  | return setAllowShrinking(false); | 
|  | } | 
|  |  | 
|  | public B setCheckDiscarded(boolean checkDiscarded) { | 
|  | this.checkDiscarded = checkDiscarded; | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public B setCheckDiscarded() { | 
|  | return setCheckDiscarded(true); | 
|  | } | 
|  |  | 
|  | public B unsetCheckDiscarded() { | 
|  | return setCheckDiscarded(false); | 
|  | } | 
|  |  | 
|  | public B setRequireAccessModificationForRepackaging( | 
|  | boolean requireAccessModificationForRepackaging) { | 
|  | this.requireAccessModificationForRepackaging = requireAccessModificationForRepackaging; | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public B requireAccessModificationForRepackaging() { | 
|  | return setRequireAccessModificationForRepackaging(true); | 
|  | } | 
|  |  | 
|  | public B unsetRequireAccessModificationForRepackaging() { | 
|  | return setRequireAccessModificationForRepackaging(false); | 
|  | } | 
|  |  | 
|  | public B setAllowAccessModification(boolean allowAccessModification) { | 
|  | this.allowAccessModification = allowAccessModification; | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public B allowAccessModification() { | 
|  | return setAllowAccessModification(true); | 
|  | } | 
|  |  | 
|  | public B disallowAccessModification() { | 
|  | return setAllowAccessModification(false); | 
|  | } | 
|  |  | 
|  | public B setAllowAnnotationRemoval(boolean allowAnnotationRemoval) { | 
|  | this.allowAnnotationRemoval = allowAnnotationRemoval; | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public B allowAnnotationRemoval() { | 
|  | return setAllowAnnotationRemoval(true); | 
|  | } | 
|  |  | 
|  | public B disallowAnnotationRemoval() { | 
|  | return setAllowAnnotationRemoval(false); | 
|  | } | 
|  |  | 
|  | private B setAllowSignatureRemoval(boolean allowSignatureRemoval) { | 
|  | this.allowSignatureRemoval = allowSignatureRemoval; | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public B allowSignatureRemoval() { | 
|  | return setAllowSignatureRemoval(true); | 
|  | } | 
|  |  | 
|  | public B disallowSignatureRemoval() { | 
|  | return setAllowSignatureRemoval(false); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Joiner to construct monotonically increasing keep info object. */ | 
|  | public abstract static class Joiner< | 
|  | J extends Joiner<J, B, K>, B extends Builder<B, K>, K extends KeepInfo<B, K>> { | 
|  |  | 
|  | abstract J self(); | 
|  |  | 
|  | final B builder; | 
|  |  | 
|  | /** | 
|  | * The set of reasons and rules that have contributed to setting {@link Builder#allowShrinking} | 
|  | * to false on this joiner. These are needed to report the correct -whyareyoukeeping reasons for | 
|  | * rooted items. | 
|  | * | 
|  | * <p>An item should only have allowShrinking set to false if it is kept by a -keep rule or the | 
|  | * {@link Enqueuer} detects a reflective access to the item (hence the {@link | 
|  | * Set<ReflectiveUseFrom>}). | 
|  | * | 
|  | * <p>These are only needed for the interpretation of keep rules into keep info, and is | 
|  | * therefore not stored in the keep info builder above. | 
|  | */ | 
|  | final Set<ReflectiveUseFrom> reasons = new HashSet<>(); | 
|  |  | 
|  | final Set<ProguardKeepRuleBase> rules = Sets.newIdentityHashSet(); | 
|  |  | 
|  | Joiner(B builder) { | 
|  | this.builder = builder; | 
|  | } | 
|  |  | 
|  | public J applyIf(boolean condition, Consumer<J> thenConsumer) { | 
|  | if (condition) { | 
|  | thenConsumer.accept(self()); | 
|  | } | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public KeepClassInfo.Joiner asClassJoiner() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public KeepFieldInfo.Joiner asFieldJoiner() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public static KeepFieldInfo.Joiner asFieldJoinerOrNull(Joiner<?, ?, ?> joiner) { | 
|  | return joiner != null ? joiner.asFieldJoiner() : null; | 
|  | } | 
|  |  | 
|  | public KeepMethodInfo.Joiner asMethodJoiner() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public Set<ReflectiveUseFrom> getReasons() { | 
|  | return reasons; | 
|  | } | 
|  |  | 
|  | public Set<ProguardKeepRuleBase> getRules() { | 
|  | return rules; | 
|  | } | 
|  |  | 
|  | public boolean isBottom() { | 
|  | return builder.isEqualTo(builder.getBottomInfo()); | 
|  | } | 
|  |  | 
|  | public boolean isCheckDiscardedEnabled() { | 
|  | return builder.isCheckDiscardedEnabled(); | 
|  | } | 
|  |  | 
|  | public boolean isOptimizationAllowed() { | 
|  | return builder.isOptimizationAllowed(); | 
|  | } | 
|  |  | 
|  | public boolean isShrinkingAllowed() { | 
|  | return builder.isShrinkingAllowed(); | 
|  | } | 
|  |  | 
|  | public boolean isTop() { | 
|  | return builder.isEqualTo(builder.getTopInfo()); | 
|  | } | 
|  |  | 
|  | public J top() { | 
|  | builder.makeTop(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J addReason(ReflectiveUseFrom reason) { | 
|  | reasons.add(reason); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J addRule(ProguardKeepRuleBase rule) { | 
|  | rules.add(rule); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J disallowAccessModification() { | 
|  | builder.disallowAccessModification(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J disallowAnnotationRemoval() { | 
|  | builder.disallowAnnotationRemoval(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J disallowMinification() { | 
|  | builder.disallowMinification(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J disallowRepackaging() { | 
|  | builder.disallowRepackaging(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J disallowOptimization() { | 
|  | builder.disallowOptimization(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J disallowShrinking() { | 
|  | builder.disallowShrinking(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J disallowSignatureRemoval() { | 
|  | builder.disallowSignatureRemoval(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J setCheckDiscarded() { | 
|  | builder.setCheckDiscarded(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J requireAccessModificationForRepackaging() { | 
|  | builder.requireAccessModificationForRepackaging(); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | public J merge(J joiner) { | 
|  | Builder<B, K> builder = joiner.builder; | 
|  | applyIf(!builder.isAccessModificationAllowed(), Joiner::disallowAccessModification); | 
|  | applyIf(!builder.isAnnotationRemovalAllowed(), Joiner::disallowAnnotationRemoval); | 
|  | applyIf(!builder.isMinificationAllowed(), Joiner::disallowMinification); | 
|  | applyIf(!builder.isOptimizationAllowed(), Joiner::disallowOptimization); | 
|  | applyIf(!builder.isRepackagingAllowed(), Joiner::disallowRepackaging); | 
|  | applyIf(!builder.isShrinkingAllowed(), Joiner::disallowShrinking); | 
|  | applyIf(!builder.isSignatureRemovalAllowed(), Joiner::disallowSignatureRemoval); | 
|  | applyIf(builder.isCheckDiscardedEnabled(), Joiner::setCheckDiscarded); | 
|  | applyIf( | 
|  | builder.isAccessModificationRequiredForRepackaging(), | 
|  | Joiner::requireAccessModificationForRepackaging); | 
|  | reasons.addAll(joiner.reasons); | 
|  | rules.addAll(joiner.rules); | 
|  | return self(); | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("unchecked") | 
|  | public J mergeUnsafe(Joiner<?, ?, ?> joiner) { | 
|  | return merge((J) joiner); | 
|  | } | 
|  |  | 
|  | public K join() { | 
|  | K joined = builder.build(); | 
|  | K original = builder.original; | 
|  | assert original.isLessThanOrEquals(joined); | 
|  | return joined; | 
|  | } | 
|  |  | 
|  | public boolean verifyShrinkingDisallowedWithRule(InternalOptions options) { | 
|  | assert !isShrinkingAllowed(); | 
|  | assert !getReasons().isEmpty() || !getRules().isEmpty() || !options.isShrinking(); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } |