blob: d523bb6e04b803b86c9a96bbbf0a2706cb273c13 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.jack.shrob.shrink;
import com.android.jack.Jack;
import com.android.jack.ir.ast.Annotable;
import com.android.jack.ir.ast.JAbstractMethodBody;
import com.android.jack.ir.ast.JAbstractStringLiteral;
import com.android.jack.ir.ast.JAlloc;
import com.android.jack.ir.ast.JAnnotation;
import com.android.jack.ir.ast.JAnnotationLiteral;
import com.android.jack.ir.ast.JArrayType;
import com.android.jack.ir.ast.JBinaryOperation;
import com.android.jack.ir.ast.JClass;
import com.android.jack.ir.ast.JClassLiteral;
import com.android.jack.ir.ast.JClassOrInterface;
import com.android.jack.ir.ast.JConstructor;
import com.android.jack.ir.ast.JDefinedClass;
import com.android.jack.ir.ast.JDefinedClassOrInterface;
import com.android.jack.ir.ast.JDefinedEnum;
import com.android.jack.ir.ast.JDefinedInterface;
import com.android.jack.ir.ast.JDynamicCastOperation;
import com.android.jack.ir.ast.JEnumLiteral;
import com.android.jack.ir.ast.JField;
import com.android.jack.ir.ast.JFieldId;
import com.android.jack.ir.ast.JFieldNameLiteral;
import com.android.jack.ir.ast.JFieldRef;
import com.android.jack.ir.ast.JInstanceOf;
import com.android.jack.ir.ast.JInterface;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JMethodCall;
import com.android.jack.ir.ast.JMethodId;
import com.android.jack.ir.ast.JMethodNameLiteral;
import com.android.jack.ir.ast.JModifier;
import com.android.jack.ir.ast.JNameValuePair;
import com.android.jack.ir.ast.JNewArray;
import com.android.jack.ir.ast.JNewInstance;
import com.android.jack.ir.ast.JNode;
import com.android.jack.ir.ast.JParameter;
import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
import com.android.jack.ir.ast.JType;
import com.android.jack.ir.ast.JTypeStringLiteral;
import com.android.jack.ir.ast.JVariable;
import com.android.jack.ir.ast.JVisitor;
import com.android.sched.item.Description;
import com.android.sched.marker.LocalMarkerManager;
import com.android.sched.util.log.LoggerFactory;
import com.android.sched.util.log.TracerFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* A visitor that traces dependencies
*/
@Description("traces dependencies")
public abstract class Tracer extends JVisitor {
@Nonnull
protected static final com.android.sched.util.log.Tracer tracer = TracerFactory.getTracer();
private final boolean traceEnclosingMethod;
@Nonnull
public Logger logger = LoggerFactory.getLogger();
public abstract boolean markIfNecessary(@Nonnull JNode node);
public abstract boolean isMarked(@Nonnull JNode node);
public Tracer(boolean traceEnclosingMethod) {
this.traceEnclosingMethod = traceEnclosingMethod;
}
public void trace(@Nonnull JType t) {
if (t instanceof JDefinedClassOrInterface) {
trace((JDefinedClassOrInterface) t);
} else if (t instanceof JArrayType) {
trace(((JArrayType) t).getLeafType());
}
}
private void traceAnnotations(@Nonnull Annotable annotable) {
for (JAnnotationLiteral a : annotable.getAnnotations()) {
trace(a);
}
}
private boolean isNullaryConstructor(@Nonnull JMethod m) {
return m instanceof JConstructor && m.getParams().isEmpty();
}
protected void traceImplementation(
@Nonnull JDefinedClass extendingOrImplementingClass, @Nonnull JClassOrInterface superClOrI) {
if (superClOrI instanceof JDefinedClassOrInterface) {
JDefinedClassOrInterface definedSuperClOrI = (JDefinedClassOrInterface) superClOrI;
for (JMethod method : definedSuperClOrI.getMethods()) {
if (isMarked(method) && mustTraceOverridingMethod(method)) {
JMethodId methodId = method.getMethodId();
JType returnType = method.getType();
JMethod implementation =
findImplementation(methodId, returnType, extendingOrImplementingClass);
if (implementation != null) {
trace(methodId, implementation.getEnclosingType(), returnType,
true /* mustTraceOverridingMethods */);
}
}
}
JClass superClass = definedSuperClOrI.getSuperClass();
if (superClass != null) {
traceImplementation(extendingOrImplementingClass, superClass);
}
for (JInterface i : definedSuperClOrI.getImplements()) {
traceImplementation(extendingOrImplementingClass, i);
}
}
}
protected abstract boolean mustTraceOverridingMethod(@Nonnull JMethod method);
protected abstract void setMustTraceOverridingMethods(@Nonnull JMethod method);
protected void trace(@Nonnull JDefinedClassOrInterface t) {
if (markIfNecessary(t)) {
traceAnnotations(t);
for (JMethod m : t.getMethods()) {
if (!isMarked(m) && (JMethod.isClinit(m) || isNullaryConstructor(m))) {
trace(m);
}
}
if (t instanceof JDefinedClass) {
JDefinedClass definedClass = (JDefinedClass) t;
JClass superClass = definedClass.getSuperClass();
if (superClass != null) {
traceImplementation(definedClass, superClass);
}
for (JInterface i : definedClass.getImplements()) {
traceImplementation(definedClass, i);
}
if (JModifier.isAnonymousType(t.getModifier())) {
trace(t.getEnclosingType());
if (traceEnclosingMethod) {
JMethod enclosingMethod = ((JDefinedClass) t).getEnclosingMethod();
if (enclosingMethod != null) {
trace(enclosingMethod);
}
}
}
if (t instanceof JDefinedEnum) {
// The values() method is needed for the switches on enum support
JMethod values = definedClass.getMethod("values", definedClass.getArray());
trace(values);
}
}
}
}
protected void trace(@Nonnull JField f) {
if (markIfNecessary(f)) {
trace(f.getEnclosingType());
trace(f.getType());
traceAnnotations(f);
}
}
protected void trace(@Nonnull JFieldId fid, @Nonnull JClassOrInterface receiverType) {
trace(receiverType);
JField field = fid.getField();
if (field != null) {
trace(field);
}
}
@CheckForNull
protected JMethod findMethod(@Nonnull JMethodId methodId,
@Nonnull JClassOrInterface enclosingType, @Nonnull JType returnType) {
for (JMethod m : methodId.getMethods()) {
if (m.getEnclosingType() == enclosingType && m.getType() == returnType) {
return m;
}
}
return null;
}
/**
* Traces the methods corresponding to a method id whose enclosing type is a subclass of
* receiverType
* @param mid the methodId of the searched method
* @param receiverType the type with which the methodId was used
* @param returnType the return type of the searched method
* @param mustTraceOverridingMethods indicates if the overriding methods of the traced method
* should be traced as well
*/
protected void trace(@Nonnull JMethodId mid, @Nonnull JClassOrInterface receiverType,
@Nonnull JType returnType, boolean mustTraceOverridingMethods) {
for (JType paramType : mid.getParamTypes()) {
trace(paramType);
}
JMethod foundMethod = findMethod(mid, receiverType, returnType);
if (foundMethod != null) {
trace(foundMethod);
if (mustTraceOverridingMethods) {
setMustTraceOverridingMethods(foundMethod);
}
}
if (receiverType instanceof JDefinedClassOrInterface && mustTraceOverridingMethods) {
ExtendingOrImplementingClassMarker marker =
((LocalMarkerManager) receiverType).getMarker(ExtendingOrImplementingClassMarker.class);
if (marker != null) {
for (JDefinedClass subClass : marker.getExtendingOrImplementingClasses()) {
if (isMarked(subClass)) {
JMethod implementation = findImplementation(mid, returnType, subClass);
if (implementation != null) {
trace(implementation);
setMustTraceOverridingMethods(implementation);
}
}
}
}
}
}
protected void trace(@Nonnull JMethod m) {
if (markIfNecessary(m)) {
trace(m.getEnclosingType());
traceAnnotations(m);
for (JParameter arg : m.getParams()) {
trace(arg.getType());
}
trace(m.getType());
for (JType type : m.getThrownExceptions()) {
trace(type);
}
if (!m.isExternal()) {
JAbstractMethodBody body = m.getBody();
if (body != null) {
accept(body);
}
}
}
}
protected void trace(@Nonnull JAnnotationLiteral al) {
JAnnotation type = al.getType();
trace(type);
for (JNameValuePair pair : al.getNameValuePairs()) {
for (JMethod method : pair.getMethodId().getMethods()) {
if (method.getEnclosingType() == type) {
trace(method);
}
}
}
}
@Override
public void endVisit(@Nonnull JFieldRef fr) {
trace(fr.getFieldId(), fr.getReceiverType());
JField field = fr.getFieldId().getField();
if (field != null) {
trace(field);
}
}
@Override
public void endVisit(@Nonnull JMethodCall mc) {
JType returnType = mc.getType();
trace(returnType);
JMethodId methodId = mc.getMethodId();
JClassOrInterface receiverType = mc.getReceiverType();
trace(receiverType);
JMethod implementationOrDefinition = null;
if (receiverType instanceof JDefinedClass) {
implementationOrDefinition = findImplementationOrDefinition(
methodId, returnType, (JDefinedClass) receiverType);
if (implementationOrDefinition == null && !receiverType.isExternal()) {
logger.log(Level.WARNING,
"No implementation or definition found for method {0} in {1} or its super types",
new Object[] {Jack.getUserFriendlyFormatter().getName(methodId.getName(),
methodId.getParamTypes(), returnType),
Jack.getUserFriendlyFormatter().getName(receiverType)});
}
} else if (receiverType instanceof JDefinedInterface) {
implementationOrDefinition =
findDefinition(methodId, returnType, (JDefinedClassOrInterface) receiverType);
if (implementationOrDefinition == null && !receiverType.isExternal()) {
logger.log(Level.WARNING,
"No implementation or definition found for method {0} in {1} or its super types",
new Object[] {Jack.getUserFriendlyFormatter().getName(methodId.getName(),
methodId.getParamTypes(), returnType),
Jack.getUserFriendlyFormatter().getName(receiverType)});
}
}
JClassOrInterface tracingStartingPoint = null;
if (implementationOrDefinition != null) {
tracingStartingPoint = implementationOrDefinition.getEnclosingType();
} else {
tracingStartingPoint = receiverType;
}
trace(tracingStartingPoint);
trace(methodId, tracingStartingPoint, returnType, true /* mustTraceOverridingMethods */);
}
@Override
public void endVisit(@Nonnull JNewInstance newInstance) {
JClass returnType = newInstance.getType();
trace(returnType);
JMethodId methodId = newInstance.getMethodId();
trace(methodId, returnType, JPrimitiveTypeEnum.VOID.getType(),
false /* mustTraceOverridingMethods */);
}
/**
* Look up the super interfaces of a type to find a method with a matching methodId and
* return type.
*
* @param methodId
* @param returnType
* @param receiverType
* @return the method was found
*/
@CheckForNull
private JMethod findDefinition(@Nonnull JMethodId methodId,
@Nonnull JType returnType, @Nonnull JDefinedClassOrInterface receiverType) {
JMethod foundMethod = findMethod(methodId, receiverType, returnType);
if (foundMethod != null) {
return foundMethod;
}
for (JInterface i : receiverType.getImplements()) {
if (i instanceof JDefinedInterface) {
JMethod foundDefinition =
findDefinition(methodId, returnType, (JDefinedInterface) i);
if (foundDefinition != null) {
return foundDefinition;
}
}
}
return null;
}
@CheckForNull
private JMethod findImplementation(
@Nonnull JMethodId methodId, @Nonnull JType returnType, @Nonnull JDefinedClass receiverType) {
JClass currentType = receiverType;
while (currentType instanceof JDefinedClass) {
JMethod foundMethod = findMethod(methodId, currentType, returnType);
if (foundMethod != null) {
return foundMethod;
}
currentType = ((JDefinedClass) currentType).getSuperClass();
}
return null;
}
/**
* Look up the super classes of a type to find a method with a matching methodId.
*
* @param methodId
* @param receiverType
* @return the type where the method was found
*/
@CheckForNull
private JMethod findImplementationOrDefinition(
@Nonnull JMethodId methodId, @Nonnull JType returnType, @Nonnull JDefinedClass receiverType) {
JMethod implementation =
findImplementation(methodId, returnType, receiverType);
if (implementation != null) {
return implementation;
}
JClass currentType = receiverType;
while (currentType instanceof JDefinedClass) {
JMethod definition =
findDefinition(methodId, returnType, (JDefinedClassOrInterface) currentType);
if (definition != null) {
return definition;
}
currentType = ((JDefinedClass) currentType).getSuperClass();
}
return null;
}
@Override
public void endVisit(@Nonnull JMethodNameLiteral mnl) {
trace(mnl.getMethod());
}
@Override
public void endVisit(@Nonnull JFieldNameLiteral fnl) {
trace(fnl.getField());
}
@Override
public void endVisit(@Nonnull JTypeStringLiteral tsl) {
trace(tsl.getReferencedType());
}
@Override
public void endVisit(@Nonnull JAlloc alloc) {
trace(alloc.getInstanceType());
}
@Override
public void endVisit(@Nonnull JAnnotationLiteral annotationLiteral) {
trace(annotationLiteral.getType());
}
@Override
public void endVisit(@Nonnull JBinaryOperation x) {
trace(x.getType());
}
@Override
public void endVisit(@Nonnull JDynamicCastOperation x) {
trace(x.getType());
}
@Override
public void endVisit(@Nonnull JClassLiteral x) {
trace(x.getRefType());
}
@Override
public void endVisit(@Nonnull JEnumLiteral enumLit) {
JField field = enumLit.getFieldId().getField();
if (field != null) {
this.accept(field);
}
super.endVisit(enumLit);
}
@Override
public void endVisit(@Nonnull JInstanceOf x) {
trace(x.getTestType());
}
@Override
public void endVisit(@Nonnull JNewArray x) {
trace(x.getArrayType());
}
@Override
public void endVisit(@Nonnull JAbstractStringLiteral x) {
trace(x.getType());
}
@Override
public void endVisit(@Nonnull JVariable x) {
trace(x.getType());
}
}