blob: 013eef10e78ca1c535dc9f71994ab916fbfd52e8 [file] [log] [blame]
/*
* Copyright (C) 2015 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.transformations;
import com.android.jack.Jack;
import com.android.jack.ir.ast.JClass;
import com.android.jack.ir.ast.JClassOrInterface;
import com.android.jack.ir.ast.JDefinedClass;
import com.android.jack.ir.ast.JDefinedClassOrInterface;
import com.android.jack.ir.ast.JDefinedInterface;
import com.android.jack.ir.ast.JInterface;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JSession;
import com.android.jack.library.TypeInInputLibraryLocation;
import com.android.jack.lookup.JLookup;
import com.android.jack.transformations.request.Remove;
import com.android.jack.transformations.request.TransformationRequest;
import com.android.sched.item.Synchronized;
import com.android.sched.schedulable.RunnableSchedulable;
import com.android.sched.util.location.Location;
import com.android.sched.util.log.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* Abstract schedulable removing a selection of types.
*/
@Synchronized
public abstract class TypeRemover implements RunnableSchedulable<JDefinedClassOrInterface> {
@Nonnull
private static final Logger logger = LoggerFactory.getLogger();
@Nonnull
private final JLookup lookup = Jack.getSession().getLookup();
@Nonnull
private final JSession session = Jack.getSession();
protected boolean isTypeFromClasspath(@Nonnull JDefinedClassOrInterface type) {
Location location = type.getLocation();
return (location instanceof TypeInInputLibraryLocation) && session.getLibraryOnClasspath()
.contains(((TypeInInputLibraryLocation) location).getInputLibrary());
}
private void updateSuperTypeList(@Nonnull JDefinedClassOrInterface type) {
if (type instanceof JDefinedClass) {
JClass superClass = type.getSuperClass();
while (mustBeRemovedInternal(superClass)) {
assert superClass != null;
for (JInterface i : ((JDefinedClass) superClass).getImplements()) {
addImplements(type, i);
}
superClass = ((JDefinedClass) superClass).getSuperClass();
}
((JDefinedClass) type).setSuperClass(superClass);
}
List<JInterface> implementsCopy = new ArrayList<JInterface>(type.getImplements());
for (JInterface i : implementsCopy) {
if (mustBeRemovedInternal(i)) {
JDefinedInterface jDefinedInterface = (JDefinedInterface) i;
type.remove(jDefinedInterface);
for (JInterface subInterface : jDefinedInterface.getImplements()) {
addImplements(type, subInterface);
}
}
}
}
private boolean mustBeRemovedInternal(@CheckForNull JClassOrInterface type) {
if (type instanceof JDefinedClassOrInterface) {
return mustBeRemoved((JDefinedClassOrInterface) type);
}
return false;
}
protected abstract boolean mustBeRemoved(@Nonnull JDefinedClassOrInterface type);
protected abstract boolean isPlannedForRemoval(@Nonnull JMethod method);
private void addImplements(@Nonnull JDefinedClassOrInterface type, @Nonnull JInterface i) {
if (!type.getImplements().contains(i)) {
if (!mustBeRemovedInternal(i)) {
type.addImplements(i);
} else {
for (JInterface subInterface : ((JDefinedInterface) i).getImplements()) {
addImplements(type, subInterface);
}
}
}
}
@Override
public synchronized void run(@Nonnull JDefinedClassOrInterface type) {
boolean toRemove = mustBeRemoved(type);
if (toRemove) {
TransformationRequest request = new TransformationRequest(type);
request.append(new Remove(type));
logger.log(Level.INFO, "Removed type {0}", Jack.getUserFriendlyFormatter().getName(type));
JClassOrInterface enclosing = type.getEnclosingType();
if (enclosing instanceof JDefinedClassOrInterface) {
JDefinedClassOrInterface enclosingType = (JDefinedClassOrInterface) enclosing;
enclosingType.removeMemberType(type);
}
Jack.getSession().removeTypeToEmit(type);
request.commit();
} else {
updateSuperTypeList(type);
updateEnclosingType(type);
if (type instanceof JDefinedClass) {
updateEnclosingMethod((JDefinedClass) type);
}
}
lookup.removeType(type);
}
private void updateEnclosingType(@Nonnull JDefinedClassOrInterface type) {
JClassOrInterface enclosingType = type.getEnclosingType();
while (enclosingType instanceof JDefinedClassOrInterface) {
if (!mustBeRemovedInternal(enclosingType) || isTypeFromClasspath(type)) {
break;
}
enclosingType = ((JDefinedClassOrInterface) enclosingType).getEnclosingType();
}
type.setEnclosingType(enclosingType);
}
private void updateEnclosingMethod(@Nonnull JDefinedClass type) {
JMethod enclosingMethod = type.getEnclosingMethod();
if (enclosingMethod != null && isPlannedForRemoval(enclosingMethod)) {
assert enclosingMethod.getEnclosingType().isToEmit();
type.setEnclosingMethod(null);
}
}
}