| package com.android.tools.r8.verticalclassmerging; |
| |
| import com.android.tools.r8.graph.AppInfoWithClassHierarchy; |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexClassAndField; |
| import com.android.tools.r8.graph.DexClassAndMethod; |
| import com.android.tools.r8.graph.DexField; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.MethodResolutionResult; |
| import com.android.tools.r8.graph.ProgramMethod; |
| import com.android.tools.r8.graph.UseRegistryWithResult; |
| import com.android.tools.r8.graph.lens.GraphLens; |
| import com.android.tools.r8.graph.lens.MethodLookupResult; |
| import com.android.tools.r8.utils.OptionalBool; |
| |
| // Searches for a reference to a non-private, non-public class, field or method declared in the |
| // same package as [source]. |
| public class IllegalAccessDetector extends UseRegistryWithResult<Boolean, ProgramMethod> { |
| |
| private final AppView<? extends AppInfoWithClassHierarchy> appViewWithClassHierarchy; |
| private final GraphLens codeLens; |
| |
| public IllegalAccessDetector( |
| AppView<? extends AppInfoWithClassHierarchy> appViewWithClassHierarchy, |
| ProgramMethod context) { |
| super(appViewWithClassHierarchy, context, false); |
| this.appViewWithClassHierarchy = appViewWithClassHierarchy; |
| this.codeLens = context.getDefinition().getCode().getCodeLens(appViewWithClassHierarchy); |
| } |
| |
| protected boolean checkFoundPackagePrivateAccess() { |
| assert getResult(); |
| return true; |
| } |
| |
| protected boolean setFoundPackagePrivateAccess() { |
| setResult(true); |
| return true; |
| } |
| |
| protected static boolean continueSearchForPackagePrivateAccess() { |
| return false; |
| } |
| |
| private boolean checkFieldReference(DexField field) { |
| return checkRewrittenFieldReference( |
| appViewWithClassHierarchy.graphLens().lookupField(field, codeLens)); |
| } |
| |
| private boolean checkRewrittenFieldReference(DexField field) { |
| assert field.getHolderType().isClassType(); |
| DexType fieldHolder = field.getHolderType(); |
| if (fieldHolder.isSamePackage(getContext().getHolderType())) { |
| if (checkRewrittenTypeReference(fieldHolder)) { |
| return checkFoundPackagePrivateAccess(); |
| } |
| DexClassAndField resolvedField = |
| appViewWithClassHierarchy.appInfo().resolveField(field).getResolutionPair(); |
| if (resolvedField == null) { |
| return setFoundPackagePrivateAccess(); |
| } |
| if (resolvedField.getHolder() != getContext().getHolder() |
| && !resolvedField.getAccessFlags().isPublic()) { |
| return setFoundPackagePrivateAccess(); |
| } |
| if (checkRewrittenFieldType(resolvedField)) { |
| return checkFoundPackagePrivateAccess(); |
| } |
| } |
| return continueSearchForPackagePrivateAccess(); |
| } |
| |
| protected boolean checkRewrittenFieldType(DexClassAndField field) { |
| return continueSearchForPackagePrivateAccess(); |
| } |
| |
| private boolean checkRewrittenMethodReference( |
| DexMethod rewrittenMethod, OptionalBool isInterface) { |
| DexType baseType = |
| rewrittenMethod.getHolderType().toBaseType(appViewWithClassHierarchy.dexItemFactory()); |
| if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) { |
| if (checkRewrittenTypeReference(rewrittenMethod.getHolderType())) { |
| return checkFoundPackagePrivateAccess(); |
| } |
| MethodResolutionResult resolutionResult = |
| isInterface.isUnknown() |
| ? appViewWithClassHierarchy |
| .appInfo() |
| .unsafeResolveMethodDueToDexFormat(rewrittenMethod) |
| : appViewWithClassHierarchy |
| .appInfo() |
| .resolveMethod(rewrittenMethod, isInterface.isTrue()); |
| if (!resolutionResult.isSingleResolution()) { |
| return setFoundPackagePrivateAccess(); |
| } |
| DexClassAndMethod resolvedMethod = resolutionResult.asSingleResolution().getResolutionPair(); |
| if (resolvedMethod.getHolder() != getContext().getHolder() |
| && !resolvedMethod.getAccessFlags().isPublic()) { |
| return setFoundPackagePrivateAccess(); |
| } |
| } |
| return continueSearchForPackagePrivateAccess(); |
| } |
| |
| private boolean checkTypeReference(DexType type) { |
| return internalCheckTypeReference(type, appViewWithClassHierarchy.graphLens(), codeLens); |
| } |
| |
| private boolean checkRewrittenTypeReference(DexType type) { |
| return internalCheckTypeReference( |
| type, GraphLens.getIdentityLens(), GraphLens.getIdentityLens()); |
| } |
| |
| private boolean internalCheckTypeReference( |
| DexType type, GraphLens graphLens, GraphLens codeLens) { |
| DexType baseType = |
| graphLens.lookupType(type.toBaseType(appViewWithClassHierarchy.dexItemFactory()), codeLens); |
| if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) { |
| DexClass clazz = appViewWithClassHierarchy.definitionFor(baseType); |
| if (clazz == null || !clazz.isPublic()) { |
| return setFoundPackagePrivateAccess(); |
| } |
| } |
| return continueSearchForPackagePrivateAccess(); |
| } |
| |
| @Override |
| public void registerInitClass(DexType clazz) { |
| if (appViewWithClassHierarchy.initClassLens().isFinal()) { |
| // The InitClass lens is always rewritten up until the most recent graph lens, so first map |
| // the class type to the most recent graph lens. |
| DexType rewrittenType = appViewWithClassHierarchy.graphLens().lookupType(clazz, codeLens); |
| DexField initClassField = |
| appViewWithClassHierarchy.initClassLens().getInitClassField(rewrittenType); |
| checkRewrittenFieldReference(initClassField); |
| } else { |
| checkTypeReference(clazz); |
| } |
| } |
| |
| @Override |
| public void registerInvokeVirtual(DexMethod method) { |
| MethodLookupResult lookup = |
| appViewWithClassHierarchy.graphLens().lookupInvokeVirtual(method, getContext(), codeLens); |
| checkRewrittenMethodReference(lookup.getReference(), OptionalBool.FALSE); |
| } |
| |
| @Override |
| public void registerInvokeDirect(DexMethod method) { |
| MethodLookupResult lookup = |
| appViewWithClassHierarchy.graphLens().lookupInvokeDirect(method, getContext(), codeLens); |
| checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN); |
| } |
| |
| @Override |
| public void registerInvokeStatic(DexMethod method) { |
| MethodLookupResult lookup = |
| appViewWithClassHierarchy.graphLens().lookupInvokeStatic(method, getContext(), codeLens); |
| checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN); |
| } |
| |
| @Override |
| public void registerInvokeInterface(DexMethod method) { |
| MethodLookupResult lookup = |
| appViewWithClassHierarchy.graphLens().lookupInvokeInterface(method, getContext(), codeLens); |
| checkRewrittenMethodReference(lookup.getReference(), OptionalBool.TRUE); |
| } |
| |
| @Override |
| public void registerInvokeSuper(DexMethod method) { |
| MethodLookupResult lookup = |
| appViewWithClassHierarchy.graphLens().lookupInvokeSuper(method, getContext(), codeLens); |
| checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN); |
| } |
| |
| @Override |
| public void registerInstanceFieldWrite(DexField field) { |
| checkFieldReference(field); |
| } |
| |
| @Override |
| public void registerInstanceFieldRead(DexField field) { |
| checkFieldReference(field); |
| } |
| |
| @Override |
| public void registerNewInstance(DexType type) { |
| checkTypeReference(type); |
| } |
| |
| @Override |
| public void registerStaticFieldRead(DexField field) { |
| checkFieldReference(field); |
| } |
| |
| @Override |
| public void registerStaticFieldWrite(DexField field) { |
| checkFieldReference(field); |
| } |
| |
| @Override |
| public void registerTypeReference(DexType type) { |
| checkTypeReference(type); |
| } |
| |
| @Override |
| public void registerInstanceOf(DexType type) { |
| checkTypeReference(type); |
| } |
| } |