diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/GraphBasedCallGraph.java b/sootup.callgraph/src/main/java/sootup/callgraph/GraphBasedCallGraph.java index d6672716bbe..822a6d00529 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/GraphBasedCallGraph.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/GraphBasedCallGraph.java @@ -65,7 +65,7 @@ public GraphBasedCallGraph() { signatureToVertex = new HashMap<>(); } - public GraphBasedCallGraph( + protected GraphBasedCallGraph( @Nonnull DefaultDirectedGraph graph, @Nonnull Map signatureToVertex) { this.graph = graph; @@ -181,9 +181,13 @@ public String exportAsDot() { Vertex targetVertex = graph.getEdgeTarget(edge); dotFormatBuilder .append("\t") - .append("\"" + sourceVertex.methodSignature + "\"") + .append("\"") + .append(sourceVertex.methodSignature) + .append("\"") .append(" -> ") - .append("\"" + targetVertex.methodSignature + "\"") + .append("\"") + .append(targetVertex.methodSignature) + .append("\"") .append(";\n"); }); @@ -207,7 +211,8 @@ public MutableCallGraph copy() { @Nonnull protected Vertex vertexOf(@Nonnull MethodSignature method) { Vertex methodVertex = signatureToVertex.get(method); - Preconditions.checkNotNull(methodVertex, "Node for " + method + " has not been added yet"); + Preconditions.checkNotNull( + methodVertex, "Node for " + method + " does not exist in this CallGraph."); return methodVertex; } diff --git a/sootup.core/src/main/java/sootup/core/util/StringTools.java b/sootup.core/src/main/java/sootup/core/util/StringTools.java index 95742044b0e..74306eaaa13 100644 --- a/sootup.core/src/main/java/sootup/core/util/StringTools.java +++ b/sootup.core/src/main/java/sootup/core/util/StringTools.java @@ -30,20 +30,15 @@ public class StringTools { /** Returns fromString, but with non-isalpha() characters printed as '\\unnnn'. */ public static String getEscapedStringOf(String fromString) { - // TODO: [ms] possible performance+ maybe(!) work on .charAt(..) instead of .toCharArray)(..) char[] fromStringArray = fromString.toCharArray(); - // TODO: [ms] this makes the exported jimple platform dependent? improve! - char cr = lineSeparator.charAt(0); - char lf = lineSeparator.length() == 2 ? lineSeparator.charAt(1) : cr; - - // find if there is (find the first) a need to escape + // find the first char that has to be escaped int firstNonAlphaPos = -1; final int size = fromStringArray.length; for (int j = 0; j < size; j++) { char ch = fromStringArray[j]; final boolean isPrintableAscii = (ch >= 32 && ch <= 126); - if (!((isPrintableAscii || ch == cr || ch == lf) && ch != '\\')) { + if (!((isPrintableAscii || ch == '\r' || ch == '\n') && ch != '\\')) { firstNonAlphaPos = j; break; } @@ -65,7 +60,7 @@ public static String getEscapedStringOf(String fromString) { j < fromStringArrayLength; j++) { char ch = fromStringArray[j]; - if (((ch >= 32 && ch <= 126) || ch == cr || ch == lf) && ch != '\\') { + if (((ch >= 32 && ch <= 126) || ch == '\r' || ch == '\n') && ch != '\\') { sb.append(ch); } else { sb.append(getUnicodeStringFromChar(ch)); @@ -75,62 +70,55 @@ public static String getEscapedStringOf(String fromString) { return sb.toString(); } - /** Convenience field storing the system line separator. */ - public static final String lineSeparator = System.getProperty("line.separator"); - /** * Returns fromString, but with certain characters printed as if they were in a Java string * literal. Used by StringConstant.toString() */ - public static String getQuotedStringOf(String fromString, boolean needsQuotes) { + public static String getQuotedStringOf(String fromString, boolean neededQuotes) { // We definitely need fromString.length + 2, but let's have some // additional space StringBuilder builder = new StringBuilder(fromString.length() + 20); - builder.append("\""); + builder.append('\''); for (int i = 0; i < fromString.length(); i++) { char ch = fromString.charAt(i); if (ch == '\\') { builder.append("\\\\"); - needsQuotes = true; + neededQuotes = true; } else if (ch == '\'') { - builder.append("\\\'"); - needsQuotes = true; + builder.append("\\'"); + neededQuotes = true; } else if (ch == '\"') { builder.append("\\\""); - needsQuotes = true; + neededQuotes = true; } else if (ch == '\n') { builder.append("\\n"); - needsQuotes = true; + neededQuotes = true; } else if (ch == '\t') { builder.append("\\t"); - needsQuotes = true; + neededQuotes = true; } /* * 04.04.2006 mbatch added handling of \r, as compilers throw error if unicode */ else if (ch == '\r') { builder.append("\\r"); - needsQuotes = true; + neededQuotes = true; } /* * 10.04.2006 Nomait A Naeem added handling of \f, as compilers throw error if unicode */ else if (ch == '\f') { builder.append("\\f"); - needsQuotes = true; + neededQuotes = true; } else if (ch >= 32 && ch <= 126 /* is printable ascii */) { builder.append(ch); - // TODO: [ms] adapt this list to add quotes in cases where it is necessary - if (ch == ' ' || ch == ';' || ch == '/') { - needsQuotes = true; - } } else { builder.append(getUnicodeStringFromChar(ch)); } } - return needsQuotes - ? builder.append('"').toString() + return neededQuotes + ? builder.append('\'').toString() : builder.subSequence(1, builder.length()).toString(); } diff --git a/sootup.core/src/main/java/sootup/core/util/Utils.java b/sootup.core/src/main/java/sootup/core/util/Utils.java index 0f02d4d6d2e..1662bfec6ea 100644 --- a/sootup.core/src/main/java/sootup/core/util/Utils.java +++ b/sootup.core/src/main/java/sootup/core/util/Utils.java @@ -23,6 +23,7 @@ */ import java.io.*; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -36,6 +37,8 @@ import javax.annotation.Nullable; import javax.tools.JavaCompiler; import javax.tools.ToolProvider; + +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringEscapeUtils; import sootup.core.jimple.basic.EquivTo; import sootup.core.model.Body; @@ -46,6 +49,16 @@ /** @author Linghui Luo */ public class Utils { + void exportAsJimpleFile(@Nonnull SootClass c, @Nonnull String baseDir){ + try { + String printClass = c.print(); + File file = new File(baseDir + c.getName().replace('.', '/') + ".jimple"); + FileUtils.writeStringToFile(file, printClass, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + @Nullable Path compileJavaOTF(String className, String javaSourceContent) { File sourceFile; diff --git a/sootup.core/src/main/java/sootup/core/util/printer/AbstractStmtPrinter.java b/sootup.core/src/main/java/sootup/core/util/printer/AbstractStmtPrinter.java index f4157a37bcc..70eb6d38a9f 100644 --- a/sootup.core/src/main/java/sootup/core/util/printer/AbstractStmtPrinter.java +++ b/sootup.core/src/main/java/sootup/core/util/printer/AbstractStmtPrinter.java @@ -128,7 +128,7 @@ public void typeSignature(@Nonnull Type type) { ((ArrayType) type).toString(this); } else { // primitive types: there should be no need to escape sth - output.append(type.toString()); + output.append(type); } } else { output.append(Jimple.escape(type.toString())); diff --git a/sootup.core/src/main/java/sootup/core/util/printer/JimplePrinter.java b/sootup.core/src/main/java/sootup/core/util/printer/JimplePrinter.java index 79896aae900..06755bb381d 100644 --- a/sootup.core/src/main/java/sootup/core/util/printer/JimplePrinter.java +++ b/sootup.core/src/main/java/sootup/core/util/printer/JimplePrinter.java @@ -134,10 +134,13 @@ public void printTo(SootClass cl, PrintWriter out, LabeledStmtPrinter printer */ EnumSet modifiers = EnumSet.copyOf(cl.getModifiers()); - // remove unwanted modifier combinations + // remove unwanted modifier combinations from print if (cl.isInterface() && ClassModifier.isAbstract(modifiers)) { modifiers.remove(ClassModifier.ABSTRACT); } + modifiers.remove(ClassModifier.SUPER); + modifiers.remove(ClassModifier.MODULE); + if (modifiers.size() != 0) { printer.modifier(ClassModifier.toString(modifiers)); printer.literal(" "); @@ -226,7 +229,7 @@ public void printTo(SootClass cl, PrintWriter out, LabeledStmtPrinter printer out.println(); } - out.println(printer.toString()); + out.println(printer); } private void printMethods(SootClass cl, LabeledStmtPrinter printer) { @@ -239,17 +242,19 @@ private void printMethods(SootClass cl, LabeledStmtPrinter printer) { while (methodIt.hasNext()) { SootMethod method = (SootMethod) methodIt.next(); + printer.handleIndent(); + method.toString(printer); + if (method.hasBody()) { Body body = method.getBody(); // print method's full signature information - method.toString(printer); printer.newline(); + incJimpleLnNum(); printBody(body, printer); } else { - printer.handleIndent(); - method.toString(printer); printer.literal(";"); + printer.newline(); incJimpleLnNum(); } @@ -343,7 +348,7 @@ private void printStmts(StmtGraph stmtGraph, LabeledStmtPrinter printer) { final boolean currentStmtHasLabel = labels.get(currentStmt) != null; if (previousStmt.branches() - || stmtGraph.predecessors(currentStmt).size() != 1 + || stmtGraph.predecessors(currentStmt).size() > 1 || previousStmt.getExpectedSuccessorCount() == 0 || currentStmtHasLabel) { printer.newline(); diff --git a/sootup.core/src/main/java/sootup/core/util/printer/LegacyJimplePrinter.java b/sootup.core/src/main/java/sootup/core/util/printer/LegacyJimplePrinter.java index bd760e3c436..68a5147280a 100644 --- a/sootup.core/src/main/java/sootup/core/util/printer/LegacyJimplePrinter.java +++ b/sootup.core/src/main/java/sootup/core/util/printer/LegacyJimplePrinter.java @@ -22,8 +22,14 @@ * #L% */ +import com.google.common.collect.ImmutableSet; +import java.util.Set; +import javax.annotation.Nonnull; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.jimple.javabytecode.stmt.JSwitchStmt; +import sootup.core.types.ClassType; +import sootup.core.types.Type; +import sootup.core.util.StringTools; /** * StmtPrinter implementation for normal (full) Jimple for OldSoot @@ -35,18 +41,112 @@ */ public class LegacyJimplePrinter extends NormalStmtPrinter { + // source: + // https://github.com/soot-oss/soot/blob/1ad74494974165e8b5f2286c90f218a00eadc243/eclipse/ca.mcgill.sable.soot/src/ca/mcgill/sable/soot/editors/JimpleScanner.java + Set soot_jimple_keywords = + ImmutableSet.of( + "ignored", + "abstract", + "final", + "native", + "public", + "protected", + "private", + "static", + "synchronized", + "transient", + "volatile", + "class", + "interface", + "void", + "boolean", + "byte", + "short", + "char", + "int", + "long", + "float", + "double", + "null_type", + "unknown", + "extends", + "implements", + "breakpoint", + "case", + "catch", + "cmp", + "cmpg", + "cmpl", + "default", + "entermonitor", + "exitmonitor", + "goto", + "if", + "instanceof", + "interfaceinvoke", + "lengthof", + "lookupswitch", + "neg", + "new", + "newarray", + "newmultiarray", + "nop", + "ret", + "return", + "specialinvoke", + "staticinvoke", + "tableswitch", + "throw", + "throws", + "virtualinvoke", + "null", + "from", + "to", + "with", + "annotation", + "enum"); + public LegacyJimplePrinter() { super(); } + @Nonnull + protected String sootEscape(String str) { + if (str.length() == 0) { + return "''"; + } + return StringTools.getQuotedStringOf(str, soot_jimple_keywords.contains(str)); + } + @Override void enableImports(boolean enable) { if (enable) { - throw new RuntimeException( + throw new IllegalArgumentException( "Imports are not supported in Legacy Jimple: don't enable UseImports"); } } + @Override + public void typeSignature(@Nonnull Type type) { + handleIndent(); + if (type instanceof ClassType) { + ClassType ctype = (ClassType) type; + final String[] splits = ctype.getPackageName().getPackageName().split("\\."); + for (String split : splits) { + if (split.length() == 0) { + continue; + } + output.append(sootEscape(split)); + output.append('.'); + } + output.append(sootEscape(ctype.getClassName())); + + } else { + // primitivetypes + output.append(type); + } + } + @Override public void stmt(Stmt currentStmt) { startStmt(currentStmt); diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java index 2db3af36940..03113909f7a 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java @@ -3,6 +3,7 @@ import java.util.Collections; import org.junit.Test; import sootup.core.inputlocation.AnalysisInputLocation; +import sootup.core.model.Body; import sootup.core.model.SootMethod; import sootup.core.model.SourceType; import sootup.core.signatures.MethodSignature; @@ -14,7 +15,7 @@ public class RuntimeJarConversionTests { - private static void execute(String methodSignature1) { + private static Body execute(String methodSignature1) { AnalysisInputLocation inputLocation = new DefaultRTJarAnalysisInputLocation( SourceType.Library, BytecodeClassLoadingOptions.Default.getBodyInterceptors()); @@ -25,7 +26,7 @@ private static void execute(String methodSignature1) { JavaView view = new JavaView(Collections.singletonList(inputLocation)); final SootMethod sootMethod = view.getMethod(methodSignature).get(); - sootMethod.getBody(); + return sootMethod.getBody(); } @Test @@ -69,4 +70,11 @@ public void testTrapsicwStubFactoryFactoryStaticImpl() { public void testFileDescriptorCloseAll() { execute(""); } + + @Test + public void testAbstractCollection_contains() { + // https://github.com/soot-oss/SootUp/issues/631 but for kotlin... + final Body body = execute(""); + System.out.println(body); + } } diff --git a/sootup.java.core/src/test/java/sootup/java/core/printer/LegacyJimplePrinterTest.java b/sootup.java.core/src/test/java/sootup/java/core/printer/LegacyJimplePrinterTest.java index ab1ceb607ea..627d3bc9ce8 100644 --- a/sootup.java.core/src/test/java/sootup/java/core/printer/LegacyJimplePrinterTest.java +++ b/sootup.java.core/src/test/java/sootup/java/core/printer/LegacyJimplePrinterTest.java @@ -28,6 +28,10 @@ public class LegacyJimplePrinterTest { SootClass buildClass(Body.BodyBuilder builder) { + return buildClass(builder, "dummyMain", "main"); + } + + SootClass buildClass(Body.BodyBuilder builder, String className, String methodName) { View view = new JavaView(new EagerInputLocation<>()); @@ -60,7 +64,7 @@ SootClass buildClass(Body.BodyBuilder builder) { null, null, null, - view.getIdentifierFactory().getClassType("dummyMain"), + view.getIdentifierFactory().getClassType(className), new EagerInputLocation()), SourceType.Application); } @@ -157,4 +161,24 @@ public void testValidOptions() { new JimplePrinter(JimplePrinter.Option.UseImports, JimplePrinter.Option.LegacyMode); p.printTo(buildClass(Body.builder()), new PrintWriter(new StringWriter())); } + + @Test + public void testLegacyEscaping() { + StringWriter out = new StringWriter(); + PrintWriter writer = new PrintWriter(out); + JimplePrinter printer = new JimplePrinter(JimplePrinter.Option.LegacyMode); + + SootClass clazz = buildClass(Body.builder(), "dummyMain", "from"); + printer.printTo(clazz, writer); + String jimple = out.toString(); + assertEquals( + "public class dummyMain\n" + + "{\n" + + " public static void 'from'()\n" + + " {\n" + + " }\n" + + "}\n" + + "\n", + jimple); + } } diff --git a/sootup.jimple.parser/src/main/java/sootup/jimple/parser/JimpleAnalysisInputLocation.java b/sootup.jimple.parser/src/main/java/sootup/jimple/parser/JimpleAnalysisInputLocation.java index e609114c9bd..6e9b5c666eb 100644 --- a/sootup.jimple.parser/src/main/java/sootup/jimple/parser/JimpleAnalysisInputLocation.java +++ b/sootup.jimple.parser/src/main/java/sootup/jimple/parser/JimpleAnalysisInputLocation.java @@ -30,11 +30,11 @@ public class JimpleAnalysisInputLocation bodyInterceptors; /** Variable to track if user has specified the SourceType. By default, it will be set to null. */ - private SourceType srcType = null; + private final SourceType srcType; // TODO: allow pointing to a single file public JimpleAnalysisInputLocation(@Nonnull Path path) { - this(path, null); + this(path, SourceType.Application); } public JimpleAnalysisInputLocation(@Nonnull Path path, @Nullable SourceType srcType) { @@ -54,19 +54,9 @@ public JimpleAnalysisInputLocation( + "' does not exist."); } this.path = path; - this.bodyInterceptors = bodyInterceptors; - setSpecifiedAsBuiltInByUser(srcType); - } - - /** - * The method sets the value of the variable srcType. - * - * @param srcType the source type for the path can be Library, Application, Phantom. - */ - public void setSpecifiedAsBuiltInByUser(@Nullable SourceType srcType) { this.srcType = srcType; + this.bodyInterceptors = bodyInterceptors; } - @Override public SourceType getSourceType() { return srcType;