/* Soot - a J*va Optimization Framework * Copyright (C) 1997-2000 Etienne Gagnon. * Copyright (C) 2008 Ben Bellamy * Copyright (C) 2008 Eric Bodden * * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * Modified by the Sable Research Group and others 1997-1999. * See the 'credits' file distributed with Soot for the complete list of * contributors. (Soot is distributed at http://www.sable.mcgill.ca/soot) */ package soot.jimple.toolkits.typing; import soot.*; import soot.options.*; import soot.jimple.*; import java.util.*; /** * This transformer assigns types to local variables. * @author Etienne Gagnon * @author Ben Bellamy * @author Eric Bodden */ public class TypeAssigner extends BodyTransformer { private boolean ignoreWrongStaticNess; public TypeAssigner(Singletons.Global g) { } public static TypeAssigner v() { return G.v().soot_jimple_toolkits_typing_TypeAssigner(); } public boolean ignoreWrongStaticNess() { return ignoreWrongStaticNess; } /** Assign types to local variables. * */ protected void internalTransform(Body b, String phaseName, Map options) { if (b == null) { throw new NullPointerException(); } Date start = new Date(); if (Options.v().verbose()) G.v().out.println("[TypeAssigner] typing system started on " + start); JBTROptions opt = new JBTROptions(options); ignoreWrongStaticNess = opt.ignore_wrong_staticness(); /* * Setting this guard to true enables comparison of the original and new * type assigners. This will be slow since type assignment will always * happen twice. The actual types used for Jimple are determined by the * use-old-type-assigner option. * * Each comparison is written as a separate semicolon-delimited line to * the standard output, and the first field is always 'cmp' for use in * grep. The format is: * * cmp;Method Name;Stmt Count;Old Inference Time (ms); New Inference * Time (ms);Typing Comparison * * The Typing Comparison field compares the old and new typings: -2 - * Old typing contains fewer variables (BAD!) -1 - Old typing is tighter * (BAD!) 0 - Typings are equal 1 - New typing is tighter 2 - New typing * contains fewer variables 3 - Typings are incomparable (inspect * manually) * * In a final release this guard, and anything in the first branch, * would probably be removed. */ if (opt.compare_type_assigners()) { compareTypeAssigners(b,opt.use_older_type_assigner()); } else { if (opt.use_older_type_assigner()) TypeResolver.resolve((JimpleBody) b, Scene.v()); else (new soot.jimple.toolkits.typing.fast.TypeResolver( (JimpleBody) b)).inferTypes(); } Date finish = new Date(); if (Options.v().verbose()) { long runtime = finish.getTime() - start.getTime(); long mins = runtime / 60000; long secs = (runtime % 60000) / 1000; G.v().out.println("[TypeAssigner] typing system ended. It took " + mins + " mins and " + secs + " secs."); } if (typingFailed((JimpleBody) b)) throw new RuntimeException("type inference failed!"); } private void compareTypeAssigners(Body b, boolean useOlderTypeAssigner) { JimpleBody jb = (JimpleBody) b, oldJb, newJb; int size = jb.getUnits().size(); long oldTime, newTime; if (useOlderTypeAssigner) { // Use old type assigner last newJb = (JimpleBody) jb.clone(); newTime = System.currentTimeMillis(); (new soot.jimple.toolkits.typing.fast.TypeResolver(newJb)) .inferTypes(); newTime = System.currentTimeMillis() - newTime; oldTime = System.currentTimeMillis(); TypeResolver.resolve(jb, Scene.v()); oldTime = System.currentTimeMillis() - oldTime; oldJb = jb; } else { // Use new type assigner last oldJb = (JimpleBody) jb.clone(); oldTime = System.currentTimeMillis(); TypeResolver.resolve(oldJb, Scene.v()); oldTime = System.currentTimeMillis() - oldTime; newTime = System.currentTimeMillis(); (new soot.jimple.toolkits.typing.fast.TypeResolver(jb)) .inferTypes(); newTime = System.currentTimeMillis() - newTime; newJb = jb; } int cmp; if (newJb.getLocals().size() < oldJb.getLocals().size()) cmp = 2; else if (newJb.getLocals().size() > oldJb.getLocals().size()) cmp = -2; else cmp = compareTypings(oldJb, newJb); G.v().out.println("cmp;" + jb.getMethod() + ";" + size + ";" + oldTime + ";" + newTime + ";" + cmp); } private boolean typingFailed(JimpleBody b) { // Check to see if any locals are untyped { Iterator<Local> localIt = b.getLocals().iterator(); while (localIt.hasNext()) { Local l = localIt.next(); if (l.getType().equals(UnknownType.v()) || l.getType().equals(ErroneousType.v())) { return true; } } } return false; } /* Returns -1 if a < b, +1 if b < a, 0 if a = b and 3 otherwise. */ private static int compareTypings(JimpleBody a, JimpleBody b) { int r = 0; Iterator<Local> ib = b.getLocals().iterator(); for (Local v : a.getLocals()) { Type ta = v.getType(), tb = ib.next().getType(); if (soot.jimple.toolkits.typing.fast.TypeResolver .typesEqual(ta, tb)) continue; /* * Sometimes there is no reason to choose between the char and byte / * short types. Enabling this check allows one algorithm to select * char and the other to select byte / short without returning * incomparable. */ else if (true && ((ta instanceof CharType && (tb instanceof ByteType || tb instanceof ShortType)) || (tb instanceof CharType && (ta instanceof ByteType || ta instanceof ShortType)))) continue; else if (soot.jimple.toolkits.typing.fast.AugHierarchy.ancestor_( ta, tb)) { if (r == -1) return 3; else r = 1; } else if (soot.jimple.toolkits.typing.fast.AugHierarchy.ancestor_( tb, ta)) { if (r == 1) return 3; else r = -1; } else return 3; } return r; } }