diff --git a/src/main/java/soot/JimpleBodyPack.java b/src/main/java/soot/JimpleBodyPack.java index 7767c20e639..b2a25a56f2f 100644 --- a/src/main/java/soot/JimpleBodyPack.java +++ b/src/main/java/soot/JimpleBodyPack.java @@ -92,6 +92,7 @@ private void applyPhaseOptions(JimpleBody b, Map opts) { pacman.getTransform("jb.lp").apply(b); // LocalPacker pacman.getTransform("jb.ne").apply(b); // NopEliminator pacman.getTransform("jb.uce").apply(b); // UnreachableCodeEliminator: Again, we might have new dead code + pacman.getTransform("jb.cp").apply(b); // CopyPropagator // LocalNameStandardizer: After all these changes, some locals // may end up being eliminated. If we want a stable local iteration diff --git a/src/main/java/soot/asm/AsmMethodSource.java b/src/main/java/soot/asm/AsmMethodSource.java index 22c41a640d7..15f7ed2f9e8 100644 --- a/src/main/java/soot/asm/AsmMethodSource.java +++ b/src/main/java/soot/asm/AsmMethodSource.java @@ -21,6 +21,7 @@ * . * #L% */ + import static org.objectweb.asm.Opcodes.ACONST_NULL; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.ANEWARRAY; diff --git a/src/main/java/soot/dexpler/DexBody.java b/src/main/java/soot/dexpler/DexBody.java index efad1e9630c..96c84219a04 100755 --- a/src/main/java/soot/dexpler/DexBody.java +++ b/src/main/java/soot/dexpler/DexBody.java @@ -29,14 +29,18 @@ import static soot.dexpler.instructions.InstructionFactory.fromInstruction; +import com.google.common.collect.ArrayListMultimap; + import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -61,11 +65,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.ArrayListMultimap; - +import soot.ArrayType; import soot.Body; import soot.DoubleType; import soot.FloatType; +import soot.IntType; import soot.Local; import soot.LongType; import soot.Modifier; @@ -94,8 +98,11 @@ import soot.dexpler.tags.DexplerTag; import soot.dexpler.tags.DoubleOpTag; import soot.dexpler.tags.FloatOpTag; +import soot.dexpler.tags.IntOrFloatOpTag; +import soot.dexpler.tags.LongOrDoubleOpTag; import soot.dexpler.typing.DalvikTyper; import soot.jimple.AddExpr; +import soot.jimple.ArrayRef; import soot.jimple.AssignStmt; import soot.jimple.BinopExpr; import soot.jimple.CastExpr; @@ -117,6 +124,7 @@ import soot.jimple.NullConstant; import soot.jimple.NumericConstant; import soot.jimple.RemExpr; +import soot.jimple.Stmt; import soot.jimple.SubExpr; import soot.jimple.internal.JIdentityStmt; import soot.jimple.toolkits.base.Aggregator; @@ -131,7 +139,8 @@ import soot.jimple.toolkits.scalar.NopEliminator; import soot.jimple.toolkits.scalar.UnconditionalBranchFolder; import soot.jimple.toolkits.scalar.UnreachableCodeEliminator; -import soot.jimple.toolkits.typing.TypeAssigner; +import soot.jimple.toolkits.typing.fast.DefaultTypingStrategy; +import soot.jimple.toolkits.typing.fast.ITypingStrategy; import soot.options.JBOptions; import soot.options.Options; import soot.tagkit.LineNumberTag; @@ -142,6 +151,8 @@ import soot.toolkits.scalar.LocalSplitter; import soot.toolkits.scalar.SharedInitializationLocalSplitter; import soot.toolkits.scalar.UnusedLocalEliminator; +import soot.util.HashMultiMap; +import soot.util.MultiMap; /** * A DexBody contains the code of a DexMethod and is used as a wrapper around JimpleBody in the jimplification process. @@ -487,6 +498,10 @@ public DexlibAbstractInstruction instructionAtAddress(int address) { return instructionAtAddress.get(key); } + protected ITypingStrategy getTypingStrategy() { + return new DefaultTypingStrategy(); + } + /** * Return the jimple equivalent of this body. * @@ -701,7 +716,7 @@ public Body jimplify(Body b, SootMethod m) { // Make sure that we don't have any overlapping uses due to returns DexReturnInliner.v().transform(jBody); - convertFloatsAndDoubles(b, jimple); + handleKnownDexTypes(b, jimple); new SharedInitializationLocalSplitter(DalvikThrowAnalysis.v()).transform(jBody); @@ -790,10 +805,67 @@ public Body jimplify(Body b, SootMethod m) { UnconditionalBranchFolder.v().transform(jBody); } DexFillArrayDataTransformer.v().transform(jBody); - // SharedInitializationLocalSplitter destroys the inserted casts, so we have to reintroduce them - convertFloatsAndDoubles(b, jimple); + //SharedInitializationLocalSplitter destroys the inserted casts, so we have to reintroduce them + MultiMap maybetypeConstraints = new HashMultiMap<>(); + handleKnownDexTypes(b, jimple); + handleKnownDexArrayTypes(b, jimple, maybetypeConstraints); + Map> definiteConstraints = new HashMap<>(); + for (Local l : b.getLocals()) { + Type type = l.getType(); + if (type instanceof PrimType) { + definiteConstraints.put(l, Collections.singleton(type)); + } + } + + new soot.jimple.toolkits.typing.fast.TypeResolver(jBody) { + + @Override + protected Collection reduceToAllowedTypesForLocal(Collection lcas, Local v) { + Collection t = definiteConstraints.get(v); + if (t != null) { + return t; + } + Set constraints = maybetypeConstraints.get(v); + if (constraints.isEmpty()) { + return lcas; + } + Set res = new HashSet<>(lcas); + res.retainAll(constraints); + if (res.isEmpty()) { + //No typing left + res.addAll(lcas); + res.addAll(constraints); + return res; + } + + return res; + } + + protected soot.jimple.toolkits.typing.fast.ITypingStrategy getTypingStrategy() { + ITypingStrategy useTyping = DexBody.this.getTypingStrategy(); + return useTyping; + } + + protected CastInsertionUseVisitor createCastInsertionUseVisitor(soot.jimple.toolkits.typing.fast.Typing tg, + soot.jimple.toolkits.typing.fast.IHierarchy h, boolean countOnly) { + return new CastInsertionUseVisitor(countOnly, jBody, tg, h) { + @Override + public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { + if (op instanceof LongConstant && useType instanceof DoubleType) { + //no cast necessary for Dex + return op; + } + if (op instanceof IntConstant && useType instanceof FloatType) { + //no cast necessary for Dex + return op; + } + return super.visit(op, useType, stmt, checkOnly); + } + }; - TypeAssigner.v().transform(jBody); + } + }.inferTypes(); + checkUnrealizableCasts(); // Shortcut: Reduce array initializations // We need to do this after typing, because otherwise we run into problems @@ -953,6 +1025,7 @@ public Body jimplify(Body b, SootMethod m) { DexReturnPacker.v().transform(jBody); for (Unit u : jBody.getUnits()) { + if (u instanceof AssignStmt) { AssignStmt ass = (AssignStmt) u; if (ass.getRightOp() instanceof CastExpr) { @@ -1009,11 +1082,78 @@ public Body jimplify(Body b, SootMethod m) { return jBody; } - public void convertFloatsAndDoubles(Body b, final Jimple jimple) { + /** + * For non-object array instructions, we know from the bytecode already what the types are, or at least + * we can reduce it to two possibilities (int/float or float/double). + * + * @param b the body + * @param jimple the jimple instance to use (caching is slightly faster) + * @param typeConstraints type constraints (these might be multiple valid possibilities) + */ + private void handleKnownDexArrayTypes(Body b, Jimple jimple, MultiMap typeConstraints) { + + Map convSingle = new HashMap<>(); UnitPatchingChain units = jBody.getUnits(); Unit u = units.getFirst(); - Local convResultFloat = null; - Local convResultDouble = null; + while (u != null) { + if (u instanceof AssignStmt) { + AssignStmt assign = ((AssignStmt) u); + Value rop = assign.getRightOp(); + if (rop instanceof ArrayRef) { + for (Tag tg : u.getTags()) { + if (tg instanceof DexplerTag) { + DexplerTag dexplerTypeTag = (DexplerTag) tg; + Type definiteType = dexplerTypeTag.getDefiniteType(); + if (definiteType != null) { + Local l = createOrGetVariableOfType(b, convSingle, definiteType); + Value prev = assign.getLeftOp(); + assign.setLeftOp(l); + units.insertAfter(jimple.newAssignStmt(prev, jimple.newCastExpr(l, definiteType)), u); + + ArrayType tp = ArrayType.v(definiteType, 1); + l = jimple.newLocal(freshLocalName("lcl" + tg.getName()), tp); + b.getLocals().add(l); + ArrayRef array = (ArrayRef) rop; + units.insertBefore(jimple.newAssignStmt(l, array.getBase()), u); + array.setBase(l); + typeConstraints.put(l, tp); + + } else if (tg instanceof IntOrFloatOpTag || tg instanceof LongOrDoubleOpTag) { + //sadly, we don't know for sure. But: we know that it's either of these two. + //we need a fresh local or each instance, no re-use allowed. + Local l = jimple.newLocal(freshLocalName("lcl" + tg.getName()), UnknownType.v()); + b.getLocals().add(l); + ArrayRef array = (ArrayRef) rop; + units.insertBefore(jimple.newAssignStmt(l, array.getBase()), u); + array.setBase(l); + if (typeConstraints != null) { + if (tg instanceof IntOrFloatOpTag) { + typeConstraints.put(l, ArrayType.v(IntType.v(), 1)); + typeConstraints.put(l, ArrayType.v(FloatType.v(), 1)); + } else { + typeConstraints.put(l, ArrayType.v(LongType.v(), 1)); + typeConstraints.put(l, ArrayType.v(DoubleType.v(), 1)); + } + } + } + } + } + } + } + u = units.getSuccOf(u); + } + } + + /** + * For several instructions, we know from the bytecode already what the types are. + * We use that knowledge here to help the type assigner. + * @param b the body + * @param jimple the jimple instance to use (caching is slightly faster) + */ + private void handleKnownDexTypes(Body b, final Jimple jimple) { + UnitPatchingChain units = jBody.getUnits(); + Unit u = units.getFirst(); + Map convSingle = new HashMap<>(); Local[] convFloat = new Local[2], convDouble = new Local[2]; while (u != null) { if (u instanceof AssignStmt) { @@ -1024,23 +1164,17 @@ public void convertFloatsAndDoubles(Body b, final Jimple jimple) { boolean isFloat = u.hasTag(FloatOpTag.NAME); if (rop instanceof AddExpr || rop instanceof SubExpr || rop instanceof MulExpr || rop instanceof DivExpr || rop instanceof RemExpr) { + Type t = null; if (isDouble) { - if (convResultDouble == null) { - convResultDouble = jimple.newLocal(freshLocalName("lclConvToDouble"), DoubleType.v()); - b.getLocals().add(convResultDouble); - } - Value prev = def.getLeftOp(); - def.setLeftOp(convResultDouble); - units.insertAfter(jimple.newAssignStmt(prev, jimple.newCastExpr(convResultDouble, DoubleType.v())), u); + t = DoubleType.v(); + } else if (isFloat) { + t = FloatType.v(); } - if (isFloat) { - if (convResultFloat == null) { - convResultFloat = jimple.newLocal(freshLocalName("lclConvToFloat"), FloatType.v()); - b.getLocals().add(convResultFloat); - } + if (t != null) { + Local l = createOrGetVariableOfType(b, convSingle, t); Value prev = def.getLeftOp(); - def.setLeftOp(convResultFloat); - units.insertAfter(jimple.newAssignStmt(prev, jimple.newCastExpr(convResultFloat, FloatType.v())), u); + def.setLeftOp(l); + units.insertAfter(jimple.newAssignStmt(prev, jimple.newCastExpr(l, t)), u); } } BinopExpr bop = (BinopExpr) rop; @@ -1088,20 +1222,62 @@ public void convertFloatsAndDoubles(Body b, final Jimple jimple) { } u = units.getSuccOf(u); } + for (Unit u1 : units) { + if (u1 instanceof AssignStmt) { + AssignStmt assign = (AssignStmt) u1; + Type tl = assign.getLeftOp().getType(); + Value rop = assign.getRightOp(); + if (rop instanceof CastExpr) { + CastExpr ce = (CastExpr) rop; + if (ce.getCastType() instanceof DoubleType) { + if (ce.getOp() instanceof LongConstant) { + LongConstant lc = (LongConstant) ce.getOp(); + long vVal = lc.value; + assign.setRightOp(DoubleConstant.v(Double.longBitsToDouble(vVal))); + } + } + if (ce.getCastType() instanceof FloatType) { + if (ce.getOp() instanceof IntConstant) { + IntConstant ic = (IntConstant) ce.getOp(); + int vVal = ic.value; + assign.setRightOp(FloatConstant.v(Float.intBitsToFloat(vVal))); + } + } + } + if (rop instanceof Constant) { + Constant c = (Constant) assign.getRightOp(); + if (tl instanceof DoubleType) { + long vVal = ((LongConstant) c).value; + assign.setRightOp(DoubleConstant.v(Double.longBitsToDouble(vVal))); + } else if (tl instanceof FloatType) { + int vVal = ((IntConstant) c).value; + assign.setRightOp(FloatConstant.v(Float.intBitsToFloat(vVal))); + } + } + } + } + } + + private Local createOrGetVariableOfType(Body b, Map map, Type t) { + Local lcl = map.get(t); + if (lcl == null) { + lcl = Jimple.v().newLocal(freshLocalName("lclConvTo" + t), t); + b.getLocals().add(lcl); + map.put(t, lcl); + } + return lcl; } /** * Removes all dexpler specific tags. Saves some memory. - * - * @param unit - * the statement + * @param unit the statement */ - protected void removeDexplerTags(Unit unit) { - for (Iterator it = unit.getTags().iterator(); it.hasNext();) { - Tag t = it.next(); + private void removeDexplerTags(Unit unit) { + for (Tag t : unit.getTags()) { if (t instanceof DexplerTag) { - it.remove(); + unit.removeTag(t.getName()); } + } } diff --git a/src/main/java/soot/dexpler/instructions/AgetInstruction.java b/src/main/java/soot/dexpler/instructions/AgetInstruction.java index d9c196cf603..5a29981e397 100644 --- a/src/main/java/soot/dexpler/instructions/AgetInstruction.java +++ b/src/main/java/soot/dexpler/instructions/AgetInstruction.java @@ -27,7 +27,6 @@ * #L% */ -import org.jf.dexlib2.Opcode; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.OneRegisterInstruction; import org.jf.dexlib2.iface.instruction.formats.Instruction23x; @@ -37,7 +36,13 @@ import soot.dexpler.DexBody; import soot.dexpler.IDalvikTyper; import soot.dexpler.InvalidDalvikBytecodeException; +import soot.dexpler.tags.BooleanOpTag; +import soot.dexpler.tags.ByteOpTag; +import soot.dexpler.tags.CharOpTag; +import soot.dexpler.tags.IntOrFloatOpTag; +import soot.dexpler.tags.LongOrDoubleOpTag; import soot.dexpler.tags.ObjectOpTag; +import soot.dexpler.tags.ShortOpTag; import soot.dexpler.typing.DalvikTyper; import soot.jimple.ArrayRef; import soot.jimple.AssignStmt; @@ -65,8 +70,28 @@ public void jimplify(DexBody body) throws InvalidDalvikBytecodeException { Local l = body.getRegisterLocal(dest); AssignStmt assign = Jimple.v().newAssignStmt(l, arrayRef); - if (aGetInstr.getOpcode() == Opcode.AGET_OBJECT) { - assign.addTag(new ObjectOpTag()); + switch (aGetInstr.getOpcode()) { + case AGET_OBJECT: + assign.addTag(new ObjectOpTag()); + break; + case AGET: + assign.addTag(new IntOrFloatOpTag()); + break; + case AGET_WIDE: + assign.addTag(new LongOrDoubleOpTag()); + break; + case AGET_BYTE: + assign.addTag(new ByteOpTag()); + break; + case AGET_CHAR: + assign.addTag(new CharOpTag()); + break; + case AGET_SHORT: + assign.addTag(new ShortOpTag()); + break; + case AGET_BOOLEAN: + assign.addTag(new BooleanOpTag()); + break; } setUnit(assign); diff --git a/src/main/java/soot/dexpler/tags/BooleanOpTag.java b/src/main/java/soot/dexpler/tags/BooleanOpTag.java new file mode 100644 index 00000000000..47a0dabb872 --- /dev/null +++ b/src/main/java/soot/dexpler/tags/BooleanOpTag.java @@ -0,0 +1,47 @@ +package soot.dexpler.tags; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 1997 - 2018 Raja Vallée-Rai and others + * %% + * This program 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 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import soot.BooleanType; +import soot.Type; +import soot.tagkit.Tag; + +public class BooleanOpTag implements Tag, DexplerTag { + + public static final String NAME = "BooleanOpTag"; + + @Override + public String getName() { + return NAME; + } + + @Override + public byte[] getValue() { + return new byte[1]; + } + + @Override + public Type getDefiniteType() { + return BooleanType.v(); + } +} diff --git a/src/main/java/soot/dexpler/tags/ByteOpTag.java b/src/main/java/soot/dexpler/tags/ByteOpTag.java new file mode 100644 index 00000000000..9f0e8c4f597 --- /dev/null +++ b/src/main/java/soot/dexpler/tags/ByteOpTag.java @@ -0,0 +1,47 @@ +package soot.dexpler.tags; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 1997 - 2018 Raja Vallée-Rai and others + * %% + * This program 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 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import soot.ByteType; +import soot.Type; +import soot.tagkit.Tag; + +public class ByteOpTag implements Tag, DexplerTag { + + public static final String NAME = "ByteOpTag"; + + @Override + public String getName() { + return NAME; + } + + @Override + public byte[] getValue() { + return new byte[1]; + } + + @Override + public Type getDefiniteType() { + return ByteType.v(); + } +} diff --git a/src/main/java/soot/dexpler/tags/CharOpTag.java b/src/main/java/soot/dexpler/tags/CharOpTag.java new file mode 100644 index 00000000000..ce7dfdcab6b --- /dev/null +++ b/src/main/java/soot/dexpler/tags/CharOpTag.java @@ -0,0 +1,47 @@ +package soot.dexpler.tags; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 1997 - 2018 Raja Vallée-Rai and others + * %% + * This program 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 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import soot.CharType; +import soot.Type; +import soot.tagkit.Tag; + +public class CharOpTag implements Tag, DexplerTag { + + public static final String NAME = "CharOpTag"; + + @Override + public String getName() { + return NAME; + } + + @Override + public byte[] getValue() { + return new byte[1]; + } + + @Override + public Type getDefiniteType() { + return CharType.v(); + } +} diff --git a/src/main/java/soot/dexpler/tags/DexplerTag.java b/src/main/java/soot/dexpler/tags/DexplerTag.java index 5c5e985b96f..08fed58efe3 100644 --- a/src/main/java/soot/dexpler/tags/DexplerTag.java +++ b/src/main/java/soot/dexpler/tags/DexplerTag.java @@ -1,5 +1,7 @@ package soot.dexpler.tags; +import soot.Type; + /*- * #%L * Soot - a J*va Optimization Framework @@ -23,5 +25,7 @@ */ public interface DexplerTag { - + public default Type getDefiniteType() { + return null; + } } diff --git a/src/main/java/soot/dexpler/tags/DoubleOpTag.java b/src/main/java/soot/dexpler/tags/DoubleOpTag.java index d99961d677d..39aeef06938 100644 --- a/src/main/java/soot/dexpler/tags/DoubleOpTag.java +++ b/src/main/java/soot/dexpler/tags/DoubleOpTag.java @@ -42,6 +42,8 @@ * #L% */ +import soot.DoubleType; +import soot.Type; import soot.tagkit.Tag; public class DoubleOpTag implements Tag, DexplerTag { @@ -57,4 +59,9 @@ public String getName() { public byte[] getValue() { return new byte[1]; } + + @Override + public Type getDefiniteType() { + return DoubleType.v(); + } } diff --git a/src/main/java/soot/dexpler/tags/FloatOpTag.java b/src/main/java/soot/dexpler/tags/FloatOpTag.java index a6220ea9a03..3dd7f36957f 100644 --- a/src/main/java/soot/dexpler/tags/FloatOpTag.java +++ b/src/main/java/soot/dexpler/tags/FloatOpTag.java @@ -42,6 +42,8 @@ * #L% */ +import soot.FloatType; +import soot.Type; import soot.tagkit.Tag; public class FloatOpTag implements Tag, DexplerTag { @@ -57,4 +59,9 @@ public String getName() { public byte[] getValue() { return new byte[1]; } + + @Override + public Type getDefiniteType() { + return FloatType.v(); + } } diff --git a/src/main/java/soot/dexpler/tags/IntOpTag.java b/src/main/java/soot/dexpler/tags/IntOpTag.java index 12bbd53ad9e..07e96b95035 100644 --- a/src/main/java/soot/dexpler/tags/IntOpTag.java +++ b/src/main/java/soot/dexpler/tags/IntOpTag.java @@ -42,6 +42,8 @@ * #L% */ +import soot.IntType; +import soot.Type; import soot.tagkit.Tag; public class IntOpTag implements Tag, DexplerTag { @@ -57,4 +59,9 @@ public String getName() { public byte[] getValue() { return new byte[1]; } + + @Override + public Type getDefiniteType() { + return IntType.v(); + } } diff --git a/src/main/java/soot/dexpler/tags/IntOrFloatOpTag.java b/src/main/java/soot/dexpler/tags/IntOrFloatOpTag.java new file mode 100644 index 00000000000..5d45cbace7e --- /dev/null +++ b/src/main/java/soot/dexpler/tags/IntOrFloatOpTag.java @@ -0,0 +1,40 @@ +package soot.dexpler.tags; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 1997 - 2018 Raja Vallée-Rai and others + * %% + * This program 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 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import soot.tagkit.Tag; + +public class IntOrFloatOpTag implements Tag, DexplerTag { + + public static final String NAME = "IntOrFloatOpTag"; + + @Override + public String getName() { + return NAME; + } + + @Override + public byte[] getValue() { + return new byte[1]; + } +} diff --git a/src/main/java/soot/dexpler/tags/LongOpTag.java b/src/main/java/soot/dexpler/tags/LongOpTag.java index 00e00aa4989..4ef524c9453 100644 --- a/src/main/java/soot/dexpler/tags/LongOpTag.java +++ b/src/main/java/soot/dexpler/tags/LongOpTag.java @@ -42,6 +42,8 @@ * #L% */ +import soot.LongType; +import soot.Type; import soot.tagkit.Tag; public class LongOpTag implements Tag, DexplerTag { @@ -57,4 +59,9 @@ public String getName() { public byte[] getValue() { return new byte[1]; } + + @Override + public Type getDefiniteType() { + return LongType.v(); + } } diff --git a/src/main/java/soot/dexpler/tags/LongOrDoubleOpTag.java b/src/main/java/soot/dexpler/tags/LongOrDoubleOpTag.java new file mode 100644 index 00000000000..2e19ff77717 --- /dev/null +++ b/src/main/java/soot/dexpler/tags/LongOrDoubleOpTag.java @@ -0,0 +1,40 @@ +package soot.dexpler.tags; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 1997 - 2018 Raja Vallée-Rai and others + * %% + * This program 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 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import soot.tagkit.Tag; + +public class LongOrDoubleOpTag implements Tag, DexplerTag { + + public static final String NAME = "LongOrDoubleOpTag"; + + @Override + public String getName() { + return NAME; + } + + @Override + public byte[] getValue() { + return new byte[1]; + } +} diff --git a/src/main/java/soot/dexpler/tags/ShortOpTag.java b/src/main/java/soot/dexpler/tags/ShortOpTag.java new file mode 100644 index 00000000000..06b518b47ea --- /dev/null +++ b/src/main/java/soot/dexpler/tags/ShortOpTag.java @@ -0,0 +1,47 @@ +package soot.dexpler.tags; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 1997 - 2018 Raja Vallée-Rai and others + * %% + * This program 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 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import soot.ShortType; +import soot.Type; +import soot.tagkit.Tag; + +public class ShortOpTag implements Tag, DexplerTag { + + public static final String NAME = "ShortOpTag"; + + @Override + public String getName() { + return NAME; + } + + @Override + public byte[] getValue() { + return new byte[1]; + } + + @Override + public Type getDefiniteType() { + return ShortType.v(); + } +} diff --git a/src/main/java/soot/jimple/spark/solver/PropWorklist.java b/src/main/java/soot/jimple/spark/solver/PropWorklist.java index 6c2261e81ea..d2005d60729 100644 --- a/src/main/java/soot/jimple/spark/solver/PropWorklist.java +++ b/src/main/java/soot/jimple/spark/solver/PropWorklist.java @@ -46,6 +46,7 @@ import soot.jimple.spark.pag.VarNode; import soot.jimple.spark.sets.P2SetVisitor; import soot.jimple.spark.sets.PointsToSetInternal; +import soot.options.Options; import soot.util.queue.QueueReader; /** @@ -66,8 +67,18 @@ public PropWorklist(PAG pag) { public void propagate() { ofcg = pag.getOnFlyCallGraph(); new TopoSorter(pag, false).sort(); + boolean ignoreErrors = Options.v().allow_cg_errors(); + logger.error("Note that we log, but continue in case of SPARK problems"); for (AllocNode object : pag.allocSources()) { - handleAllocNode(object); + try { + handleAllocNode(object); + } catch (Exception e) { + if (ignoreErrors) { + logger.error("An error occurred during SPARK worklist propagation; continuing", e); + } else { + throw e; + } + } } boolean verbose = pag.getOpts().verbose(); @@ -77,7 +88,15 @@ public void propagate() { } VarNode vsrc; while ((vsrc = varNodeWorkList.pollFirst()) != null) { - handleVarNode(vsrc); + try { + handleVarNode(vsrc); + } catch (Exception e) { + if (ignoreErrors) { + logger.error("An error occurred during SPARK worklist propagation; continuing", e); + } else { + throw e; + } + } } if (verbose) { logger.debug("Now handling field references"); @@ -99,7 +118,15 @@ public final void visit(Node n) { } HashSet edgesToPropagate = new HashSet(); for (FieldRefNode object : pag.loadSources()) { - handleFieldRefNode(object, edgesToPropagate); + try { + handleFieldRefNode(object, edgesToPropagate); + } catch (Exception e) { + if (ignoreErrors) { + logger.error("An error occurred during SPARK worklist propagation; continuing", e); + } else { + throw e; + } + } } Set nodesToFlush = Collections.newSetFromMap(new IdentityHashMap()); for (Object[] pair : edgesToPropagate) { diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/DefaultTypingStrategy.java b/src/main/java/soot/jimple/toolkits/typing/fast/DefaultTypingStrategy.java index 6e9213afd73..14160f3a3c9 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/DefaultTypingStrategy.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/DefaultTypingStrategy.java @@ -41,7 +41,6 @@ import soot.RefType; import soot.Scene; import soot.Type; -import soot.util.Chain; import soot.util.HashMultiMap; import soot.util.MultiMap; @@ -57,7 +56,7 @@ public class DefaultTypingStrategy implements ITypingStrategy { public static int USE_PARALLEL_MINIMIZE_IF_ENTRIES_MORE_THAN = 1000; @Override - public Typing createTyping(Chain locals) { + public Typing createTyping(Collection locals) { return new Typing(locals); } diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/ITypingStrategy.java b/src/main/java/soot/jimple/toolkits/typing/fast/ITypingStrategy.java index d0e60738e5c..de1e142caf0 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/ITypingStrategy.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/ITypingStrategy.java @@ -1,9 +1,9 @@ package soot.jimple.toolkits.typing.fast; +import java.util.Collection; import java.util.List; import soot.Local; -import soot.util.Chain; /*- * #%L @@ -43,7 +43,7 @@ public interface ITypingStrategy { * the locals * @return the {@link Typing} */ - public Typing createTyping(Chain locals); + public Typing createTyping(Collection locals); /** * Creates a new typing class as a copy from a given class diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java index ba8f870eb86..1e7b1b647de 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java @@ -145,8 +145,9 @@ private void addDepend(Local v, int stmtIndex) { } public void inferTypes() { + this.split_new(); ITypingStrategy typingStrategy = getTypingStrategy(); - AugEvalFunction ef = new AugEvalFunction(this.jb); + AugEvalFunction ef = createAugEvalFunction(this.jb); BytecodeHierarchy bh = new BytecodeHierarchy(); Collection sigma = this.applyAssignmentConstraints(typingStrategy.createTyping(this.jb.getLocals()), ef, bh); @@ -157,11 +158,6 @@ public void inferTypes() { int[] castCount = new int[1]; Typing tg = this.minCasts(sigma, bh, castCount); - if (castCount[0] != 0) { - this.split_new(); - sigma = this.applyAssignmentConstraints(typingStrategy.createTyping(this.jb.getLocals()), ef, bh); - tg = this.minCasts(sigma, bh, castCount); - } this.insertCasts(tg, bh, false); @@ -187,6 +183,10 @@ public void inferTypes() { } } + protected AugEvalFunction createAugEvalFunction(JimpleBody jb2) { + return new AugEvalFunction(this.jb); + } + protected ITypingStrategy getTypingStrategy() { return DefaultTypingStrategy.INSTANCE; } @@ -212,6 +212,13 @@ public CastInsertionUseVisitor(boolean countOnly, JimpleBody jb, Typing tg, IHie public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { Type t = AugEvalFunction.eval_(this.tg, op, stmt, this.jb); if (useType == t) { + if (op instanceof CastExpr) { + CastExpr ce = (CastExpr) op; + if (ce.getType() == t) { + //no cast necessary! + return ce.getOp(); + } + } return op; } @@ -511,6 +518,7 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction final DefinitionStmt stmt = this.assignments.get(defIdx); Value lhs = stmt.getLeftOp(); + Local v = (lhs instanceof Local) ? (Local) lhs : (Local) ((ArrayRef) lhs).getBase(); Type told = tg.get(v); @@ -529,7 +537,7 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction } // Special handling for exception objects with phantom types - final Collection lcas; + Collection lcas; if (!typesEqual(told, t_) && told instanceof RefType && t_ instanceof RefType && (((RefType) told).getSootClass().isPhantom() || ((RefType) t_).getSootClass().isPhantom()) && (stmt.getRightOp() instanceof CaughtExceptionRef)) { @@ -548,6 +556,7 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction } boolean addFirstDecision = false; + lcas = reduceToAllowedTypesForLocal(lcas, v); for (Type t : lcas) { if (!typesEqual(t, told)) { BitSet dependsV = this.depends.get(v); @@ -598,6 +607,14 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction return r; } + protected Collection reduceToAllowedTypesForLocal(Collection lcas, Local v) { + return lcas; + } + + protected boolean isAllowedTypeForLocal(Local v, Type t) { + return true; + } + protected ArrayDeque createSigmaQueue() { return new ArrayDeque<>(); } diff --git a/src/test/java/soot/portedtest/RedundantJimpleStatementsTest.java b/src/test/java/soot/portedtest/RedundantJimpleStatementsTest.java index ab51952a5f7..3880327cb79 100644 --- a/src/test/java/soot/portedtest/RedundantJimpleStatementsTest.java +++ b/src/test/java/soot/portedtest/RedundantJimpleStatementsTest.java @@ -22,6 +22,7 @@ * #L% */ +import com.google.common.base.Joiner; import org.junit.Ignore; import org.junit.Test; import soot.*; @@ -58,23 +59,26 @@ public void loadClasses(String first, String... more) { Options.v().set_output_format(Options.output_format_jimple); Options.v().set_allow_phantom_refs(true); Options.v().set_ignore_resolving_levels(true); + PhaseOptions.v().setPhaseOption("jb", "stabilize-local-names:true"); + Scene.v().loadNecessaryClasses(); } - private List bodyStmtsAsStrings(Body body) { - List contentList = new ArrayList<>(); - for (Unit stmt : body.getUnits()) { - contentList.add(stmt.toString()); - } - return contentList; + private String bodyStmtsAsString(Body body) { + return Joiner.on('\n').join(body.getUnits()); } private void assertJimpleStmts(SootMethod method, List expectedStmts) { Body body = method.retrieveActiveBody(); assertNotNull(body); - List actualStmts = bodyStmtsAsStrings(body); + String actualStmts = bodyStmtsAsString(body); - assertEquals(expectedStmts, actualStmts); + String exp = Joiner.on('\n').join(expectedStmts); + if (!exp.equals(actualStmts)) { + //Use a custom error message which is nicely readable; + //JUnits assertEquals mangles with the text, which makes it harder to retrieve the ground truth + throw new AssertionError(String.format("Expected:\n%s\n\nWas:\n%s", exp, actualStmts)); + } } @Test @@ -82,28 +86,28 @@ public void test01() { loadClasses(resourcePath, "java8", "bin"); SootMethod method = Scene.v().getMethod(""); List expectedBodyStmts = Stream.of( - "r6 := @this: MethodAcceptingLamExpr", - "r0 = staticinvoke ()", - "$r2 = ", - "$r1 = new java.lang.StringBuilder", - "specialinvoke $r1.()>()", - "$r3 = virtualinvoke $r1.(\"Percentage : \")", - "$d0 = interfaceinvoke r0.(45.0)", - "$r4 = virtualinvoke $r3.($d0)", - "$r5 = virtualinvoke $r4.()", - "virtualinvoke $r2.($r5)", - "return").collect(Collectors.toCollection(ArrayList::new)); + "r0 := @this: MethodAcceptingLamExpr", + "r1 = staticinvoke ()", + "$r2 = ", + "$r4 = new java.lang.StringBuilder", + "specialinvoke $r4.()>()", + "$r5 = virtualinvoke $r4.(\"Percentage : \")", + "$d0 = interfaceinvoke r1.(45.0)", + "$r6 = virtualinvoke $r5.($d0)", + "$r3 = virtualinvoke $r6.()", + "virtualinvoke $r2.($r3)", + "return" + ).collect(Collectors.toCollection(ArrayList::new)); assertJimpleStmts(method, expectedBodyStmts); } @Test public void test02() { loadClasses(resourcePath, "java9", "bin"); - List expectedBodyStmts = Stream.of( - "r1 = dynamicinvoke \"makeConcatWithConstants\" (\"This test\") (\"\\u0001 is cool\")", - "$r0 = ", - "virtualinvoke $r0.(r1)", - "return").collect(Collectors.toCollection(ArrayList::new)); + List expectedBodyStmts = Stream.of("r1 = dynamicinvoke \"makeConcatWithConstants\" (\"This test\") (\"\\u0001 is cool\")", + "$r0 = ", + "virtualinvoke $r0.(r1)", + "return").collect(Collectors.toCollection(ArrayList::new)); SootMethod method = Scene.v().getMethod(""); assertJimpleStmts(method, expectedBodyStmts); } @@ -112,14 +116,14 @@ public void test02() { public void test03() { loadClasses(resourcePath, "java11", "bin"); List expectedBodyStmts = Stream.of( - "r5 := @this: TypeInferenceLambda", - "r0 = staticinvoke ()", - "$r2 = staticinvoke (2)", - "$r1 = staticinvoke (3)", - "$r3 = interfaceinvoke r0.($r2, $r1)", - "$r4 = (java.lang.Integer) $r3", - "virtualinvoke $r4.()", - "return").collect(Collectors.toCollection(ArrayList::new)); + "r0 := @this: TypeInferenceLambda", + "r5 = staticinvoke ()", + "$r1 = staticinvoke (2)", + "$r2 = staticinvoke (3)", + "$r4 = interfaceinvoke r5.($r1, $r2)", + "$r3 = (java.lang.Integer) $r4", + "virtualinvoke $r3.()", + "return").collect(Collectors.toCollection(ArrayList::new)); SootMethod method = Scene.v().getMethod(""); assertJimpleStmts(method, expectedBodyStmts); } @@ -129,9 +133,9 @@ public void test04() { loadClasses(resourcePath, "java6", "bin"); List expectedBodyStmts = Stream.of( - "r0 := @this: Autoboxing", - "staticinvoke (6)", - "return").collect(Collectors.toCollection(ArrayList::new)); + "r0 := @this: Autoboxing", + "staticinvoke (6)", + "return").collect(Collectors.toCollection(ArrayList::new)); SootMethod method = Scene.v().getMethod(""); assertJimpleStmts(method, expectedBodyStmts); } @@ -141,27 +145,27 @@ public void test05() { loadClasses(resourcePath, "java6", "bin"); List expectedBodyStmts = Stream.of( - "r12 := @this: GenTypeParam", - "$r0 = new java.util.ArrayList", - "specialinvoke $r0.(int)>(3)", - "$r1 = newarray (java.lang.Integer)[3]", - "$r2 = staticinvoke (1)", - "$r1[0] = $r2", - "$r3 = staticinvoke (2)", - "$r1[1] = $r3", - "$r4 = staticinvoke (3)", - "$r1[2] = $r4", - "r5 = staticinvoke ($r1)", - "$r6 = new GenTypeParam", - "specialinvoke $r6.()>()", - "virtualinvoke $r6.($r0, r5)", - "$r7 = ", - "$r10 = staticinvoke (2)", - "$r9 = staticinvoke (8)", - "$r8 = staticinvoke (3)", - "$r11 = virtualinvoke $r6.($r10, $r9, $r8)", - "virtualinvoke $r7.($r11)", - "return").collect(Collectors.toCollection(ArrayList::new)); + "r00 := @this: GenTypeParam", + "$r11 = new java.util.ArrayList", + "specialinvoke $r11.(int)>(3)", + "$r09 = newarray (java.lang.Integer)[3]", + "$r03 = staticinvoke (1)", + "$r09[0] = $r03", + "$r04 = staticinvoke (2)", + "$r09[1] = $r04", + "$r05 = staticinvoke (3)", + "$r09[2] = $r05", + "r12 = staticinvoke ($r09)", + "$r01 = new GenTypeParam", + "specialinvoke $r01.()>()", + "virtualinvoke $r01.($r11, r12)", + "$r02 = ", + "$r06 = staticinvoke (2)", + "$r07 = staticinvoke (8)", + "$r08 = staticinvoke (3)", + "$r10 = virtualinvoke $r01.($r06, $r07, $r08)", + "virtualinvoke $r02.($r10)", + "return").collect(Collectors.toCollection(ArrayList::new)); SootMethod method = Scene.v().getMethod(""); assertJimpleStmts(method, expectedBodyStmts); } @@ -171,24 +175,25 @@ public void test05() { public void test06() { loadClasses(resourcePath, "java6", "bin"); List expectedBodyStmts = Stream.of( - "r9 := @this: Reflection", - "$r0 = new Reflection", - "specialinvoke $r0.()>()", - "r1 = class \"LReflection;\"", - "r11 = class \"LReflection;\"", - "r10 = class \"LReflection;\"", - "$r2 = ", - "virtualinvoke $r2.(class \"LReflection;\")", - "$r3 = newarray (java.lang.Class)[0]", - "r4 = virtualinvoke r11.($r3)", - "$r5 = ", - "$r6 = virtualinvoke r4.()", - "virtualinvoke $r5.($r6)", - "$r7 = ", - "$r8 = virtualinvoke r10.()", - "$i0 = lengthof $r8", - "virtualinvoke $r7.($i0)", - "return").collect(Collectors.toCollection(ArrayList::new)); + "r00 := @this: Reflection", + "$r01 = new Reflection", + "specialinvoke $r01.()>()", + "r05 = class \"LReflection;\"", + "r06 = class \"LReflection;\"", + "r07 = class \"LReflection;\"", + "$r02 = ", + "virtualinvoke $r02.(class \"LReflection;\")", + "$r08 = newarray (java.lang.Class)[0]", + "r10 = virtualinvoke r06.($r08)", + "$r03 = ", + "$r09 = virtualinvoke r10.()", + "virtualinvoke $r03.($r09)", + "$r04 = ", + "$r11 = virtualinvoke r07.()", + "$i00 = lengthof $r11", + "virtualinvoke $r04.($i00)", + "return" + ).collect(Collectors.toCollection(ArrayList::new)); SootMethod method = Scene.v().getMethod(""); assertJimpleStmts(method, expectedBodyStmts); } @@ -197,20 +202,20 @@ public void test06() { public void test07() { loadClasses(resourcePath, "java6", "bin"); List expectedBodyStmts = Stream.of( - "r7 := @this: UncheckedCast", - "$r0 = newarray (java.lang.Integer)[4]", - "$r1 = staticinvoke (5)", - "$r0[0] = $r1", - "$r2 = staticinvoke (8)", - "$r0[1] = $r2", - "$r3 = staticinvoke (9)", - "$r0[2] = $r3", - "$r4 = staticinvoke (6)", - "$r0[3] = $r4", - "r5 = staticinvoke ($r0)", - "$r6 = ", - "virtualinvoke $r6.(r5)", - "return").collect(Collectors.toCollection(ArrayList::new)); + "r0 := @this: UncheckedCast", + "$r6 = newarray (java.lang.Integer)[4]", + "$r2 = staticinvoke (5)", + "$r6[0] = $r2", + "$r3 = staticinvoke (8)", + "$r6[1] = $r3", + "$r4 = staticinvoke (9)", + "$r6[2] = $r4", + "$r5 = staticinvoke (6)", + "$r6[3] = $r5", + "r7 = staticinvoke ($r6)", + "$r1 = ", + "virtualinvoke $r1.(r7)", + "return").collect(Collectors.toCollection(ArrayList::new)); SootMethod method = Scene.v().getMethod(""); assertJimpleStmts(method, expectedBodyStmts); } @@ -219,14 +224,14 @@ public void test07() { public void test08() { loadClasses(resourcePath, "java11", "bin"); List expectedBodyStmts = Stream.of( - "r5 := @this: TypeInferenceLambda", - "r0 = staticinvoke ()", - "$r2 = staticinvoke (2)", - "$r1 = staticinvoke (3)", - "$r3 = interfaceinvoke r0.($r2, $r1)", - "$r4 = (java.lang.Integer) $r3", - "virtualinvoke $r4.()", - "return").collect(Collectors.toCollection(ArrayList::new)); + "r0 := @this: TypeInferenceLambda", + "r5 = staticinvoke ()", + "$r1 = staticinvoke (2)", + "$r2 = staticinvoke (3)", + "$r4 = interfaceinvoke r5.($r1, $r2)", + "$r3 = (java.lang.Integer) $r4", + "virtualinvoke $r3.()", + "return").collect(Collectors.toCollection(ArrayList::new)); SootMethod method = Scene.v().getMethod(""); assertJimpleStmts(method, expectedBodyStmts); @@ -237,16 +242,17 @@ public void test08() { public void test09() { loadClasses(resourcePath, "java9", "bin"); List expectedBodyStmts = Stream.of( - "r1 := @this: AnonymousDiamondOperator", - "$r6 = new AnonymousDiamondOperator$1", - "specialinvoke $r6.(AnonymousDiamondOperator)>(r1)", - "$r3 = staticinvoke (22)", - "$r2 = staticinvoke (23)", - "$r7 = (MyClass) $r6", - "$r4 = virtualinvoke $r7.($r3, $r2)", - "r5 = (java.lang.Integer) $r4", - "$i0 = virtualinvoke r5.()", - "return $i0").collect(Collectors.toCollection(ArrayList::new)); + "r0 := @this: AnonymousDiamondOperator", + "$r1 = new AnonymousDiamondOperator$1", + "specialinvoke $r1.(AnonymousDiamondOperator)>(r0)", + "$r3 = staticinvoke (22)", + "$r4 = staticinvoke (23)", + "$r2 = (MyClass) $r1", + "$r7 = virtualinvoke $r2.($r3, $r4)", + "$r5 = (java.lang.Integer) $r7", + "r6 = $r5", + "$i0 = virtualinvoke r6.()", + "return $i0").collect(Collectors.toCollection(ArrayList::new)); SootMethod method = Scene.v().getMethod(""); assertJimpleStmts(method, expectedBodyStmts); } @@ -255,17 +261,17 @@ public void test09() { public void test10() { loadClasses(resourcePath, "java6", "bin"); List expectedBodyStmts = Stream.of( - "r3 := @this: DeclareEnum", - "r0 = staticinvoke ()", - "i0 = lengthof r0", - "i1 = 0", - "if i1 >= i0 goto return", - "r1 = r0[i1]", - "$r2 = ", - "virtualinvoke $r2.(r1)", - "i1 = i1 + 1", - "goto [?= (branch)]", - "return").collect(Collectors.toCollection(ArrayList::new)); + "r0 := @this: DeclareEnum", + "r2 = staticinvoke ()", + "i0 = lengthof r2", + "i1 = 0", + "if i1 >= i0 goto return", + "r1 = r2[i1]", + "$r3 = ", + "virtualinvoke $r3.(r1)", + "i1 = i1 + 1", + "goto [?= (branch)]", + "return").collect(Collectors.toCollection(ArrayList::new)); SootMethod method = Scene.v().getMethod(""); assertJimpleStmts(method, expectedBodyStmts); } @@ -274,15 +280,15 @@ public void test10() { public void test11() { loadClasses(resourcePath, "java6", "bin"); List expectedBodyStmts = Stream.of( - "r1 := @this: GenericTypeParamOnClass", - "$r0 = new GenericTypeParamOnClass$A", - "specialinvoke $r0.(GenericTypeParamOnClass)>(r1)", - "$r2 = staticinvoke (5)", - "staticinvoke ($r0, $r2)", - "$r3 = virtualinvoke $r0.()", - "$r4 = (java.lang.Integer) $r3", - "virtualinvoke $r4.()", - "return").collect(Collectors.toCollection(ArrayList::new)); + "r0 := @this: GenericTypeParamOnClass", + "$r1 = new GenericTypeParamOnClass$A", + "specialinvoke $r1.(GenericTypeParamOnClass)>(r0)", + "$r2 = staticinvoke (5)", + "staticinvoke ($r1, $r2)", + "$r4 = virtualinvoke $r1.()", + "$r3 = (java.lang.Integer) $r4", + "virtualinvoke $r3.()", + "return").collect(Collectors.toCollection(ArrayList::new)); SootMethod method = Scene.v().getMethod(""); assertJimpleStmts(method, expectedBodyStmts); }