/* * This file is part of JOP, the Java Optimized Processor * see <http://www.jopdesign.com/> * * Copyright (C) 2011, Stefan Hepp (stefan@stefant.org). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.jopdesign.jcopter.optimizer; import com.jopdesign.common.AppInfo; import com.jopdesign.common.ClassInfo; import com.jopdesign.common.FieldInfo; import com.jopdesign.common.MethodInfo; import com.jopdesign.common.config.BooleanOption; import com.jopdesign.common.config.Option; import com.jopdesign.common.config.OptionGroup; import com.jopdesign.common.processormodel.ProcessorModel; import com.jopdesign.common.tools.UsedCodeFinder; import com.jopdesign.common.tools.UsedCodeFinder.Mark; import com.jopdesign.jcopter.JCopter; import org.apache.log4j.Logger; import java.util.LinkedList; import java.util.List; /** * @author Stefan Hepp (stefan@stefant.org) */ public class UnusedCodeRemover { public static final BooleanOption REMOVE_CONST_FIELDS = new BooleanOption("remove-const-fields", "Remove unused static final fields, "+ "does not (yet) check if they are used by WCA-annotations", false); public static final Option[] optionList = { REMOVE_CONST_FIELDS }; private static final Logger logger = Logger.getLogger(JCopter.LOG_OPTIMIZER+".UnusedCodeRemover"); private final JCopter jcopter; private final OptionGroup options; private UsedCodeFinder ucf; private boolean removeConstFields; public UnusedCodeRemover(JCopter jcopter, OptionGroup options) { this.jcopter = jcopter; this.options = options; removeConstFields = options.getOption(REMOVE_CONST_FIELDS); ucf = new UsedCodeFinder(); } public JCopter getJCopter() { return jcopter; } public void execute() { ucf.resetMarks(); // This starts at all app roots and JVM roots, as well as all threads, // <clinit> methods are marked in all reached classes ucf.markUsedMembers(); // We also need to mark everything else we do not want to remove .. AppInfo appInfo = AppInfo.getSingleton(); ProcessorModel pm = appInfo.getProcessorModel(); if (pm.keepJVMClasses()) { for (String clName : pm.getJVMClasses()) { ClassInfo cls = appInfo.getClassInfo(clName); if (cls != null) { ucf.markUsedMembers(cls, true); } } } removeUnusedMembers(); } /** * Remove all unused classes, methods and fields. */ private void removeUnusedMembers() { AppInfo appInfo = AppInfo.getSingleton(); // we cannot modify the lists while iterating through it List<ClassInfo> unusedClasses = new LinkedList<ClassInfo>(); List<FieldInfo> unusedFields = new LinkedList<FieldInfo>(); List<MethodInfo> unusedMethods = new LinkedList<MethodInfo>(); int fields = 0; int methods = 0; for (ClassInfo cls : appInfo.getClassInfos()) { if (ucf.getMark(cls)==Mark.UNUSED) { unusedClasses.add(cls); logger.debug("Removing unused class " +cls); continue; } unusedFields.clear(); unusedMethods.clear(); if (appInfo.isHwObject(cls)) { // Do not remove fields from hardware objects, else the mapping gets broken and // chaos takes over! logger.debug("Skipping fields of used hardware object " +cls); } else { for (FieldInfo f : cls.getFields()) { if (ucf.getMark(f)==Mark.UNUSED) { unusedFields.add(f); } } } for (MethodInfo m : cls.getMethods()) { Mark mark = ucf.getMark(m); if (mark == Mark.UNUSED) { unusedMethods.add(m); } if (mark == Mark.MARKED && !m.isNative() && !m.isAbstract()) { logger.info("Making unused method "+m+" abstract"); m.setAbstract(true); m.getClassInfo().setAbstract(true); } } for (FieldInfo f : unusedFields) { fields += removeField(f); } for (MethodInfo m : unusedMethods) { methods += removeMethod(m); } } appInfo.removeClasses(unusedClasses); int classes = unusedClasses.size(); logger.info("Removed " + classes + (classes == 1 ? " class, " : " classes, ") + fields + (fields == 1 ? " field, " : " fields, ") + methods + (methods == 1 ? " method" : " methods")); } private int removeField(FieldInfo f) { if (f.isStatic() && f.isFinal() && !removeConstFields) { logger.debug("Not removing unused static final "+f); // Instead, mark it as unused f.setUnusedAnnotation(); return 0; } else { ClassInfo cls = f.getClassInfo(); logger.debug("Removing unused field "+f); cls.removeField(f.getShortName()); return 1; } } private int removeMethod(MethodInfo m) { ClassInfo cls = m.getClassInfo(); logger.debug("Removing unused method "+m); cls.removeMethod(m.getMethodSignature()); return 1; } }