| // Copyright (c) 2019, 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.graph; | 
 |  | 
 | import com.android.tools.r8.errors.CompilationError; | 
 | import com.android.tools.r8.utils.SetUtils; | 
 | import com.google.common.collect.Sets; | 
 | import java.util.Collection; | 
 | import java.util.Collections; | 
 | import java.util.Set; | 
 | import java.util.function.BiPredicate; | 
 | import java.util.function.Consumer; | 
 |  | 
 | public abstract class ResolutionResult { | 
 |  | 
 |   /** | 
 |    * Returns true if resolution succeeded *and* the resolved method has a known definition. | 
 |    * | 
 |    * <p>Note that {@code !isSingleResolution() && !isFailedResolution()} can be true. In that case | 
 |    * that resolution has succeeded, but the definition of the resolved method is unknown. In | 
 |    * particular this is the case for the clone() method on arrays. | 
 |    */ | 
 |   public boolean isSingleResolution() { | 
 |     return false; | 
 |   } | 
 |  | 
 |   /** Returns non-null if isSingleResolution() is true, otherwise null. */ | 
 |   public SingleResolutionResult asSingleResolution() { | 
 |     return null; | 
 |   } | 
 |  | 
 |   /** | 
 |    * Returns true if resolution failed. | 
 |    * | 
 |    * <p>Note the disclaimer in the doc of {@code isSingleResolution()}. | 
 |    */ | 
 |   public boolean isFailedResolution() { | 
 |     return false; | 
 |   } | 
 |  | 
 |   /** Returns non-null if isFailedResolution() is true, otherwise null. */ | 
 |   public FailedResolutionResult asFailedResolution() { | 
 |     return null; | 
 |   } | 
 |  | 
 |   /** Short-hand to get the single resolution method if resolution finds it, null otherwise. */ | 
 |   public final DexEncodedMethod getSingleTarget() { | 
 |     return isSingleResolution() ? asSingleResolution().getResolvedMethod() : null; | 
 |   } | 
 |  | 
 |   public abstract boolean isAccessibleFrom(DexProgramClass context, AppInfoWithSubtyping appInfo); | 
 |  | 
 |   public abstract boolean isAccessibleForVirtualDispatchFrom( | 
 |       DexProgramClass context, AppInfoWithSubtyping appInfo); | 
 |  | 
 |   // TODO(b/145187573): Remove this and use proper access checks. | 
 |   @Deprecated | 
 |   public abstract boolean isVirtualTarget(); | 
 |  | 
 |   /** Lookup the single target of an invoke-special on this resolution result if possible. */ | 
 |   public abstract DexEncodedMethod lookupInvokeSpecialTarget( | 
 |       DexProgramClass context, AppInfoWithSubtyping appInfo); | 
 |  | 
 |   /** Lookup the single target of an invoke-super on this resolution result if possible. */ | 
 |   public abstract DexEncodedMethod lookupInvokeSuperTarget( | 
 |       DexProgramClass context, AppInfoWithSubtyping appInfo); | 
 |  | 
 |   @Deprecated | 
 |   public abstract DexEncodedMethod lookupInvokeSuperTarget(DexClass context, AppInfo appInfo); | 
 |  | 
 |   public final Set<DexEncodedMethod> lookupVirtualDispatchTargets( | 
 |       boolean isInterface, AppInfoWithSubtyping appInfo) { | 
 |     return isInterface ? lookupInterfaceTargets(appInfo) : lookupVirtualTargets(appInfo); | 
 |   } | 
 |  | 
 |   public abstract Set<DexEncodedMethod> lookupVirtualTargets(AppInfoWithSubtyping appInfo); | 
 |  | 
 |   public abstract Set<DexEncodedMethod> lookupInterfaceTargets(AppInfoWithSubtyping appInfo); | 
 |  | 
 |   /** Result for a resolution that succeeds with a known declaration/definition. */ | 
 |   public static class SingleResolutionResult extends ResolutionResult { | 
 |     private final DexClass initialResolutionHolder; | 
 |     private final DexClass resolvedHolder; | 
 |     private final DexEncodedMethod resolvedMethod; | 
 |  | 
 |     public SingleResolutionResult( | 
 |         DexClass initialResolutionHolder, | 
 |         DexClass resolvedHolder, | 
 |         DexEncodedMethod resolvedMethod) { | 
 |       assert initialResolutionHolder != null; | 
 |       assert resolvedHolder != null; | 
 |       assert resolvedMethod != null; | 
 |       assert resolvedHolder.type == resolvedMethod.method.holder; | 
 |       this.resolvedHolder = resolvedHolder; | 
 |       this.resolvedMethod = resolvedMethod; | 
 |       this.initialResolutionHolder = initialResolutionHolder; | 
 |     } | 
 |  | 
 |     public DexClass getResolvedHolder() { | 
 |       return resolvedHolder; | 
 |     } | 
 |  | 
 |     public DexEncodedMethod getResolvedMethod() { | 
 |       return resolvedMethod; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public boolean isSingleResolution() { | 
 |       return true; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public SingleResolutionResult asSingleResolution() { | 
 |       return this; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithSubtyping appInfo) { | 
 |       return AccessControl.isMethodAccessible( | 
 |           resolvedMethod, initialResolutionHolder, context, appInfo); | 
 |     } | 
 |  | 
 |     @Override | 
 |     public boolean isAccessibleForVirtualDispatchFrom( | 
 |         DexProgramClass context, AppInfoWithSubtyping appInfo) { | 
 |       return resolvedMethod.isVirtualMethod() && isAccessibleFrom(context, appInfo); | 
 |     } | 
 |  | 
 |     @Override | 
 |     public boolean isVirtualTarget() { | 
 |       return resolvedMethod.isVirtualMethod(); | 
 |     } | 
 |  | 
 |     /** | 
 |      * This is intended to model the actual behavior of invoke-special on a JVM. | 
 |      * | 
 |      * <p>See https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html#jvms-6.5.invokespecial | 
 |      * and comments below for deviations due to diverging behavior on actual JVMs. | 
 |      */ | 
 |     @Override | 
 |     public DexEncodedMethod lookupInvokeSpecialTarget( | 
 |         DexProgramClass context, AppInfoWithSubtyping appInfo) { | 
 |       // If the resolution is non-accessible then no target exists. | 
 |       if (isAccessibleFrom(context, appInfo)) { | 
 |         return internalInvokeSpecialOrSuper( | 
 |             context, appInfo, (sup, sub) -> isSuperclass(sup, sub, appInfo)); | 
 |       } | 
 |       return null; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Lookup the target of an invoke-super. | 
 |      * | 
 |      * <p>This will return the target iff the resolution succeeded and the target is valid (i.e., | 
 |      * non-static and non-initializer) and accessible from {@code context}. | 
 |      * | 
 |      * <p>Additionally, this will also verify that the invoke-super is valid, i.e., it is on the a | 
 |      * super type of the current context. Any invoke-special targeting the same type should have | 
 |      * been mapped to an invoke-direct, but could change due to merging so we need to still allow | 
 |      * the context to be equal to the targeted (symbolically referenced) type. | 
 |      * | 
 |      * @param context Class the invoke is contained in, i.e., the holder of the caller. | 
 |      * @param appInfo Application info. | 
 |      * @return The actual target for the invoke-super or {@code null} if no valid target is found. | 
 |      */ | 
 |     @Override | 
 |     public DexEncodedMethod lookupInvokeSuperTarget( | 
 |         DexProgramClass context, AppInfoWithSubtyping appInfo) { | 
 |       if (isAccessibleFrom(context, appInfo)) { | 
 |         return lookupInvokeSuperTarget(context.asDexClass(), appInfo); | 
 |       } | 
 |       return null; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public DexEncodedMethod lookupInvokeSuperTarget(DexClass context, AppInfo appInfo) { | 
 |       assert context != null; | 
 |       if (resolvedMethod.isInstanceInitializer() | 
 |           || (appInfo.hasSubtyping() | 
 |               && initialResolutionHolder != context | 
 |               && !isSuperclass(initialResolutionHolder, context, appInfo.withSubtyping()))) { | 
 |         throw new CompilationError( | 
 |             "Illegal invoke-super to " + resolvedMethod.toSourceString(), context.getOrigin()); | 
 |       } | 
 |       return internalInvokeSpecialOrSuper(context, appInfo, (sup, sub) -> true); | 
 |     } | 
 |  | 
 |     private DexEncodedMethod internalInvokeSpecialOrSuper( | 
 |         DexClass context, AppInfo appInfo, BiPredicate<DexClass, DexClass> isSuperclass) { | 
 |  | 
 |       // Statics cannot be targeted by invoke-special/super. | 
 |       if (getResolvedMethod().isStatic()) { | 
 |         return null; | 
 |       } | 
 |  | 
 |       // The symbolic reference is the holder type that resolution was initiated at. | 
 |       DexClass symbolicReference = initialResolutionHolder; | 
 |  | 
 |       // First part of the spec is to determine the starting point for lookup for invoke special. | 
 |       // Notice that the specification indicates that the immediate super type should | 
 |       // be used when three items hold, the second being: | 
 |       //   is-class(sym-ref) => is-super(sym-ref, context) | 
 |       // in the case of an interface that is trivially satisfied, which would lead the initial type | 
 |       // to be java.lang.Object. However in practice the lookup appears to start at the symbolic | 
 |       // reference in the case of interfaces, so the second condition should likely be interpreted: | 
 |       //   is-class(sym-ref) *and* is-super(sym-ref, context). | 
 |       final DexClass initialType; | 
 |       if (!resolvedMethod.isInstanceInitializer() | 
 |           && !symbolicReference.isInterface() | 
 |           && isSuperclass.test(symbolicReference, context)) { | 
 |         // If reference is a super type of the context then search starts at the immediate super. | 
 |         initialType = context.superType == null ? null : appInfo.definitionFor(context.superType); | 
 |       } else { | 
 |         // Otherwise it starts at the reference itself. | 
 |         initialType = symbolicReference; | 
 |       } | 
 |       // Abort if for some reason the starting point could not be found. | 
 |       if (initialType == null) { | 
 |         return null; | 
 |       } | 
 |       // 1-3. Search the initial class and its supers in order for a matching instance method. | 
 |       DexMethod method = getResolvedMethod().method; | 
 |       DexEncodedMethod target = null; | 
 |       DexClass current = initialType; | 
 |       while (current != null) { | 
 |         target = current.lookupMethod(method); | 
 |         if (target != null) { | 
 |           break; | 
 |         } | 
 |         current = current.superType == null ? null : appInfo.definitionFor(current.superType); | 
 |       } | 
 |       // 4. Otherwise, it is the single maximally specific method: | 
 |       if (target == null) { | 
 |         target = appInfo.resolveMaximallySpecificMethods(initialType, method).getSingleTarget(); | 
 |       } | 
 |       if (target == null) { | 
 |         return null; | 
 |       } | 
 |       // Linking exceptions: | 
 |       // A non-instance method throws IncompatibleClassChangeError. | 
 |       if (target.isStatic()) { | 
 |         return null; | 
 |       } | 
 |       // An instance initializer that is not to the symbolic reference throws NoSuchMethodError. | 
 |       // It appears as if this check is also in place for non-initializer methods too. | 
 |       // See NestInvokeSpecialMethodAccessWithIntermediateTest. | 
 |       if ((target.isInstanceInitializer() || target.isPrivateMethod()) | 
 |           && target.method.holder != symbolicReference.type) { | 
 |         return null; | 
 |       } | 
 |       // Runtime exceptions: | 
 |       // An abstract method throws AbstractMethodError. | 
 |       if (target.isAbstract()) { | 
 |         return null; | 
 |       } | 
 |       return target; | 
 |     } | 
 |  | 
 |     private static boolean isSuperclass(DexClass sup, DexClass sub, AppInfoWithSubtyping appInfo) { | 
 |       return sup != sub && appInfo.isSubtype(sub.type, sup.type); | 
 |     } | 
 |  | 
 |     @Override | 
 |     // TODO(b/140204899): Leverage refined receiver type if available. | 
 |     public Set<DexEncodedMethod> lookupVirtualTargets(AppInfoWithSubtyping appInfo) { | 
 |       if (resolvedMethod.isPrivateMethod()) { | 
 |         // If the resolved reference is private there is no dispatch. | 
 |         // This is assuming that the method is accessible, which implies self/nest access. | 
 |         return Collections.singleton(resolvedMethod); | 
 |       } | 
 |       assert resolvedMethod.isNonPrivateVirtualMethod(); | 
 |       // First add the target for receiver type method.type. | 
 |       Set<DexEncodedMethod> result = SetUtils.newIdentityHashSet(resolvedMethod); | 
 |       // Add all matching targets from the subclass hierarchy. | 
 |       DexMethod method = resolvedMethod.method; | 
 |       // TODO(b/140204899): Instead of subtypes of holder, we could iterate subtypes of refined | 
 |       //   receiver type if available. | 
 |       for (DexType type : appInfo.subtypes(method.holder)) { | 
 |         DexClass clazz = appInfo.definitionFor(type); | 
 |         if (!clazz.isInterface()) { | 
 |           ResolutionResult methods = appInfo.resolveMethodOnClass(clazz, method); | 
 |           DexEncodedMethod target = methods.getSingleTarget(); | 
 |           if (target != null && target.isNonPrivateVirtualMethod()) { | 
 |             result.add(target); | 
 |           } | 
 |         } | 
 |       } | 
 |       return result; | 
 |     } | 
 |  | 
 |     @Override | 
 |     // TODO(b/140204899): Leverage refined receiver type if available. | 
 |     public Set<DexEncodedMethod> lookupInterfaceTargets(AppInfoWithSubtyping appInfo) { | 
 |       if (resolvedMethod.isPrivateMethod()) { | 
 |         // If the resolved reference is private there is no dispatch. | 
 |         // This is assuming that the method is accessible, which implies self/nest access. | 
 |         assert resolvedMethod.hasCode(); | 
 |         return Collections.singleton(resolvedMethod); | 
 |       } | 
 |       assert resolvedMethod.isNonPrivateVirtualMethod(); | 
 |       Set<DexEncodedMethod> result = Sets.newIdentityHashSet(); | 
 |       // Add default interface methods to the list of targets. | 
 |       // | 
 |       // This helps to make sure we take into account synthesized lambda classes | 
 |       // that we are not aware of. Like in the following example, we know that all | 
 |       // classes, XX in this case, override B::bar(), but there are also synthesized | 
 |       // classes for lambda which don't, so we still need default method to be live. | 
 |       // | 
 |       //   public static void main(String[] args) { | 
 |       //     X x = () -> {}; | 
 |       //     x.bar(); | 
 |       //   } | 
 |       // | 
 |       //   interface X { | 
 |       //     void foo(); | 
 |       //     default void bar() { } | 
 |       //   } | 
 |       // | 
 |       //   class XX implements X { | 
 |       //     public void foo() { } | 
 |       //     public void bar() { } | 
 |       //   } | 
 |       // | 
 |       addIfDefaultMethodWithLambdaInstantiations(appInfo, resolvedMethod, result); | 
 |  | 
 |       DexMethod method = resolvedMethod.method; | 
 |       Consumer<DexEncodedMethod> addIfNotAbstract = | 
 |           m -> { | 
 |             if (!m.accessFlags.isAbstract()) { | 
 |               result.add(m); | 
 |             } | 
 |           }; | 
 |       // Default methods are looked up when looking at a specific subtype that does not override | 
 |       // them. Otherwise, we would look up default methods that are actually never used. However, we | 
 |       // have to add bridge methods, otherwise we can remove a bridge that will be used. | 
 |       Consumer<DexEncodedMethod> addIfNotAbstractAndBridge = | 
 |           m -> { | 
 |             if (!m.accessFlags.isAbstract() && m.accessFlags.isBridge()) { | 
 |               result.add(m); | 
 |             } | 
 |           }; | 
 |  | 
 |       // TODO(b/140204899): Instead of subtypes of holder, we could iterate subtypes of refined | 
 |       //   receiver type if available. | 
 |       for (DexType type : appInfo.subtypes(method.holder)) { | 
 |         DexClass clazz = appInfo.definitionFor(type); | 
 |         if (clazz.isInterface()) { | 
 |           ResolutionResult targetMethods = appInfo.resolveMethodOnInterface(clazz, method); | 
 |           if (targetMethods.isSingleResolution()) { | 
 |             // Sub-interfaces can have default implementations that override the resolved method. | 
 |             // Therefore we have to add default methods in sub interfaces. | 
 |             DexEncodedMethod singleTarget = targetMethods.getSingleTarget(); | 
 |             addIfDefaultMethodWithLambdaInstantiations(appInfo, singleTarget, result); | 
 |             addIfNotAbstractAndBridge.accept(singleTarget); | 
 |           } | 
 |         } else { | 
 |           ResolutionResult targetMethods = appInfo.resolveMethodOnClass(clazz, method); | 
 |           if (targetMethods.isSingleResolution()) { | 
 |             addIfNotAbstract.accept(targetMethods.getSingleTarget()); | 
 |           } | 
 |         } | 
 |       } | 
 |       return result; | 
 |     } | 
 |  | 
 |     private void addIfDefaultMethodWithLambdaInstantiations( | 
 |         AppInfoWithSubtyping appInfo, DexEncodedMethod method, Set<DexEncodedMethod> result) { | 
 |       if (method == null) { | 
 |         return; | 
 |       } | 
 |       if (method.hasCode()) { | 
 |         DexProgramClass holder = appInfo.definitionForProgramType(method.method.holder); | 
 |         if (appInfo.hasAnyInstantiatedLambdas(holder)) { | 
 |           result.add(method); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   abstract static class EmptyResult extends ResolutionResult { | 
 |  | 
 |     @Override | 
 |     public final DexEncodedMethod lookupInvokeSpecialTarget( | 
 |         DexProgramClass context, AppInfoWithSubtyping appInfo) { | 
 |       return null; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public DexEncodedMethod lookupInvokeSuperTarget( | 
 |         DexProgramClass context, AppInfoWithSubtyping appInfo) { | 
 |       return null; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public final DexEncodedMethod lookupInvokeSuperTarget(DexClass context, AppInfo appInfo) { | 
 |       return null; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public final Set<DexEncodedMethod> lookupVirtualTargets(AppInfoWithSubtyping appInfo) { | 
 |       return null; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public final Set<DexEncodedMethod> lookupInterfaceTargets(AppInfoWithSubtyping appInfo) { | 
 |       return null; | 
 |     } | 
 |   } | 
 |  | 
 |   /** Singleton result for the special case resolving the array clone() method. */ | 
 |   public static class ArrayCloneMethodResult extends EmptyResult { | 
 |  | 
 |     static final ArrayCloneMethodResult INSTANCE = new ArrayCloneMethodResult(); | 
 |  | 
 |     private ArrayCloneMethodResult() { | 
 |       // Intentionally left empty. | 
 |     } | 
 |  | 
 |     @Override | 
 |     public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithSubtyping appInfo) { | 
 |       return true; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public boolean isAccessibleForVirtualDispatchFrom( | 
 |         DexProgramClass context, AppInfoWithSubtyping appInfo) { | 
 |       return true; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public boolean isVirtualTarget() { | 
 |       return true; | 
 |     } | 
 |   } | 
 |  | 
 |   /** Base class for all types of failed resolutions. */ | 
 |   public abstract static class FailedResolutionResult extends EmptyResult { | 
 |  | 
 |     @Override | 
 |     public boolean isFailedResolution() { | 
 |       return true; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public FailedResolutionResult asFailedResolution() { | 
 |       return this; | 
 |     } | 
 |  | 
 |     public void forEachFailureDependency(Consumer<DexEncodedMethod> methodCausingFailureConsumer) { | 
 |       // Default failure has no dependencies. | 
 |     } | 
 |  | 
 |     @Override | 
 |     public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithSubtyping appInfo) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public boolean isAccessibleForVirtualDispatchFrom( | 
 |         DexProgramClass context, AppInfoWithSubtyping appInfo) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public boolean isVirtualTarget() { | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   public static class ClassNotFoundResult extends FailedResolutionResult { | 
 |     static final ClassNotFoundResult INSTANCE = new ClassNotFoundResult(); | 
 |  | 
 |     private ClassNotFoundResult() { | 
 |       // Intentionally left empty. | 
 |     } | 
 |   } | 
 |  | 
 |   abstract static class FailedResolutionWithCausingMethods extends FailedResolutionResult { | 
 |  | 
 |     private final Collection<DexEncodedMethod> methodsCausingError; | 
 |  | 
 |     private FailedResolutionWithCausingMethods(Collection<DexEncodedMethod> methodsCausingError) { | 
 |       this.methodsCausingError = methodsCausingError; | 
 |     } | 
 |  | 
 |     @Override | 
 |     public void forEachFailureDependency(Consumer<DexEncodedMethod> methodCausingFailureConsumer) { | 
 |       this.methodsCausingError.forEach(methodCausingFailureConsumer); | 
 |     } | 
 |   } | 
 |  | 
 |   public static class IncompatibleClassResult extends FailedResolutionWithCausingMethods { | 
 |     static final IncompatibleClassResult INSTANCE = | 
 |         new IncompatibleClassResult(Collections.emptyList()); | 
 |  | 
 |     private IncompatibleClassResult(Collection<DexEncodedMethod> methodsCausingError) { | 
 |       super(methodsCausingError); | 
 |     } | 
 |  | 
 |     static IncompatibleClassResult create(Collection<DexEncodedMethod> methodsCausingError) { | 
 |       return methodsCausingError.isEmpty() | 
 |           ? INSTANCE | 
 |           : new IncompatibleClassResult(methodsCausingError); | 
 |     } | 
 |   } | 
 |  | 
 |   public static class NoSuchMethodResult extends FailedResolutionResult { | 
 |  | 
 |     static final NoSuchMethodResult INSTANCE = new NoSuchMethodResult(); | 
 |   } | 
 |  | 
 |   public static class IllegalAccessOrNoSuchMethodResult extends FailedResolutionWithCausingMethods { | 
 |  | 
 |     public IllegalAccessOrNoSuchMethodResult(DexEncodedMethod methodCausingError) { | 
 |       super(Collections.singletonList(methodCausingError)); | 
 |       assert methodCausingError != null; | 
 |     } | 
 |   } | 
 | } |