diff --git a/shared-test-resources/bugfixes/DeadAssignmentEliminatorTest.class b/shared-test-resources/bugfixes/DeadAssignmentEliminatorTest.class index f9930c0f240..4c145afbb09 100644 Binary files a/shared-test-resources/bugfixes/DeadAssignmentEliminatorTest.class and b/shared-test-resources/bugfixes/DeadAssignmentEliminatorTest.class differ diff --git a/shared-test-resources/bugfixes/DeadAssignmentEliminatorTest.java b/shared-test-resources/bugfixes/DeadAssignmentEliminatorTest.java index ee82e76864f..baa9fd5613e 100644 --- a/shared-test-resources/bugfixes/DeadAssignmentEliminatorTest.java +++ b/shared-test-resources/bugfixes/DeadAssignmentEliminatorTest.java @@ -23,4 +23,14 @@ void tc2() { } System.out.println(x); } + + void tc3(int x) { + boolean trackReusableBuffers = false; + try { + trackReusableBuffers = "true".equals(System.getProperty("com.fasterxml.jackson.core.util.BufferRecyclers.trackReusableBuffers")); + } catch (SecurityException var2) { + } + boolean bufferRecyclerTracker = trackReusableBuffers ? true : null; + } + } \ No newline at end of file diff --git a/sootup.core/src/main/java/sootup/core/jimple/basic/Local.java b/sootup.core/src/main/java/sootup/core/jimple/basic/Local.java index 7453ff6eb33..f6f5a93f8d6 100644 --- a/sootup.core/src/main/java/sootup/core/jimple/basic/Local.java +++ b/sootup.core/src/main/java/sootup/core/jimple/basic/Local.java @@ -23,6 +23,7 @@ */ import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nonnull; import sootup.core.graph.StmtGraph; @@ -153,6 +154,20 @@ public List getDefsForLocalUse(StmtGraph graph, Stmt stmt) { return defStmts; } + public List getStmtsUsingOrDefiningthisLocal(Collection stmts, Stmt removedStmt) { + List localOccurrences = new ArrayList<>(); + for (Stmt stmt : stmts) { + if (stmt.equivTo(removedStmt)) continue; + List stmtUsesAndDefs = stmt.getUsesAndDefs().collect(Collectors.toList()); + for (Value stmtUse : stmtUsesAndDefs) { + if (stmtUse instanceof Local && stmtUse.equivTo(this)) { + localOccurrences.add(stmt); + } + } + } + return localOccurrences; + } + @Override public V accept(@Nonnull V v) { v.caseLocal(this); diff --git a/sootup.core/src/main/java/sootup/core/model/Body.java b/sootup.core/src/main/java/sootup/core/model/Body.java index 4cfc51f6565..7e8606a9088 100644 --- a/sootup.core/src/main/java/sootup/core/model/Body.java +++ b/sootup.core/src/main/java/sootup/core/model/Body.java @@ -464,7 +464,13 @@ public void removeDefLocalsOf(@Nonnull Stmt stmt) { .ifPresent( def -> { if (def instanceof Local) { - locals.remove(def); + List localOccurrences = + ((Local) def).getStmtsUsingOrDefiningthisLocal(this.graph.getStmts(), stmt); + // after removing stmt, if the local variable doesn't occur anywhere else then + // safely remove + if (localOccurrences.isEmpty()) { + locals.remove(def); + } } }); } diff --git a/sootup.interceptors/src/main/java/sootup/interceptors/LocalPacker.java b/sootup.interceptors/src/main/java/sootup/interceptors/LocalPacker.java index 5112a61f983..c61d7049707 100644 --- a/sootup.interceptors/src/main/java/sootup/interceptors/LocalPacker.java +++ b/sootup.interceptors/src/main/java/sootup/interceptors/LocalPacker.java @@ -92,6 +92,9 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) Value use = iterator.next(); if (use instanceof Local) { Local newLocal = localToNewLocal.get(use); + if (newLocal == null) { + continue; + } // assign a reasonable name if (!newLocals.contains(newLocal)) { int starPos = newLocal.getName().indexOf('*'); @@ -110,18 +113,20 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) if (defOpt.isPresent() && defOpt.get() instanceof Local) { Local def = (Local) defOpt.get(); Local newLocal = localToNewLocal.get(def); - // assign a reasonable name - if (!newLocals.contains(newLocal)) { - int starPos = newLocal.getName().indexOf('*'); - String reasonableName = newLocal.getName().substring(0, starPos) + newLocals.size(); - List oriLocals = newLocalToLocals.get(newLocal); - newLocal = newLocal.withName(reasonableName); - newLocals.add(newLocal); - for (Local ori : oriLocals) { - localToNewLocal.put(ori, newLocal); + if (newLocal != null) { + // assign a reasonable name + if (!newLocals.contains(newLocal)) { + int starPos = newLocal.getName().indexOf('*'); + String reasonableName = newLocal.getName().substring(0, starPos) + newLocals.size(); + List oriLocals = newLocalToLocals.get(newLocal); + newLocal = newLocal.withName(reasonableName); + newLocals.add(newLocal); + for (Local ori : oriLocals) { + localToNewLocal.put(ori, newLocal); + } } + newStmt = ((AbstractDefinitionStmt) newStmt).withNewDef(newLocal); } - newStmt = ((AbstractDefinitionStmt) newStmt).withNewDef(newLocal); } if (!stmt.equals(newStmt)) { stmtGraph.replaceNode(stmt, newStmt); diff --git a/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/DeadAssignmentEliminatorTest.java b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/DeadAssignmentEliminatorTest.java index 00e16ed1fed..e7820f412a1 100644 --- a/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/DeadAssignmentEliminatorTest.java +++ b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/DeadAssignmentEliminatorTest.java @@ -1,7 +1,6 @@ package sootup.java.bytecode.frontend.interceptors; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.*; import java.nio.file.Path; import java.nio.file.Paths; @@ -27,6 +26,7 @@ import sootup.core.types.PrimitiveType; import sootup.core.util.Utils; import sootup.interceptors.DeadAssignmentEliminator; +import sootup.interceptors.LocalPacker; import sootup.java.bytecode.frontend.inputlocation.PathBasedAnalysisInputLocation; import sootup.java.core.JavaIdentifierFactory; import sootup.java.core.language.JavaJimple; @@ -189,7 +189,7 @@ public void testDeadAssignmentEliminator() { assertEquals( Stream.of( "DeadAssignmentEliminatorTest this", - "unknown $stack3, $stack4, $stack5", + "unknown $stack3, $stack4, $stack5, l1, l2", "this := @this: DeadAssignmentEliminatorTest", "l1 = 30", "l2 = l1", @@ -216,7 +216,7 @@ public void testDeadAssignmentEliminator() { assertEquals( Stream.of( "DeadAssignmentEliminatorTest this", - "unknown $stack2, $stack3", + "unknown $stack2, $stack3, l1", "this := @this: DeadAssignmentEliminatorTest", "l1 = \"cde\"", "$stack2 = virtualinvoke l1.()", @@ -229,4 +229,53 @@ public void testDeadAssignmentEliminator() { .collect(Collectors.toList()), Utils.filterJimple(body1.toString())); } + + @Test + public void testLocalCountAfterDAE() { + AnalysisInputLocation inputLocation = + new PathBasedAnalysisInputLocation.ClassFileBasedAnalysisInputLocation( + classFilePath, + "", + SourceType.Application, + Arrays.asList(new DeadAssignmentEliminator(), new LocalPacker())); + JavaView view = new JavaView(Collections.singletonList(inputLocation)); + final MethodSignature methodSignature = + view.getIdentifierFactory() + .getMethodSignature( + "DeadAssignmentEliminatorTest", + "tc3", + "void", + Collections.singletonList(PrimitiveType.getInt().getName())); + + Body body = view.getMethod(methodSignature).get().getBody(); + assertTrue(body.getLocals().size() == 5); + assertFalse(body.getStmts().isEmpty()); + assertEquals( + Stream.of( + "DeadAssignmentEliminatorTest this0", + "int l1", + "unknown $stack2, l3, l4", + "this0 := @this: DeadAssignmentEliminatorTest", + "l1 := @parameter0: int", + "label1:", + "$stack2 = \"true\"", + "l3 = staticinvoke (\"com.fasterxml.jackson.core.util.BufferRecyclers.trackReusableBuffers\")", + "l4 = virtualinvoke $stack2.(l3)", + "label2:", + "goto label4", + "label3:", + "l3 := @caughtexception", + "label4:", + "if l4 == 0 goto label5", + "l3 = staticinvoke (1)", + "goto label6", + "label5:", + "l3 = null", + "label6:", + "l3 = virtualinvoke l3.()", + "return", + "catch java.lang.SecurityException from label1 to label2 with label3") + .collect(Collectors.toList()), + Utils.filterJimple(body.toString())); + } }