1+ package soot ;
2+
3+ /*-
4+ * #%L
5+ * Soot - a J*va Optimization Framework
6+ * %%
7+ * Copyright (C) 2021 Timothy Hoffman
8+ * %%
9+ * This program is free software: you can redistribute it and/or modify
10+ * it under the terms of the GNU Lesser General Public License as
11+ * published by the Free Software Foundation, either version 2.1 of the
12+ * License, or (at your option) any later version.
13+ *
14+ * This program is distributed in the hope that it will be useful,
15+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+ * GNU General Lesser Public License for more details.
18+ *
19+ * You should have received a copy of the GNU General Lesser Public
20+ * License along with this program. If not, see
21+ * <http://www.gnu.org/licenses/lgpl-2.1.html>.
22+ * #L%
23+ */
24+
25+ import java .util .Arrays ;
26+ import java .util .stream .Collectors ;
27+
28+ import org .junit .Assert ;
29+ import org .junit .Test ;
30+ import org .powermock .core .classloader .annotations .PowerMockIgnore ;
31+
32+ import soot .jimple .Stmt ;
33+ import soot .testing .framework .AbstractTestingFramework ;
34+
35+ /**
36+ * @author Timothy Hoffman
37+ */
38+ @ PowerMockIgnore ({ "com.sun.org.apache.xerces.*" , "javax.xml.*" , "org.xml.*" , "org.w3c.*" })
39+ public class AbstractSootFieldRefTest extends AbstractTestingFramework {
40+
41+ private static final String TEST_TARGET_CLASS = "soot.AbstractSootFieldRefTestInput" ;
42+
43+ @ Override
44+ protected void setupSoot () {
45+ }
46+
47+ @ Test
48+ public void testCachingInvalidation_Name () throws Exception {
49+ SootMethod m1 = prepareTarget (methodSigFromComponents (TEST_TARGET_CLASS , "void" , "m1" ), TEST_TARGET_CLASS );
50+ final SootClass clas = m1 .getDeclaringClass ();
51+
52+ // There is only 1 field in the class originally.
53+ Assert .assertEquals (Arrays .asList ("f" ),
54+ clas .getFields ().stream ().map (SootField ::getName ).sorted ().collect (Collectors .toList ()));
55+
56+ // Ensure the previous value of AbstractSootFieldRef#resolveCache
57+ // is not used if the referenced field itself is modified.
58+ final Body b = m1 .retrieveActiveBody ();
59+ final SootFieldRef fRef = getFieldRef (b );
60+ Assert .assertEquals ("f" , fRef .name ());
61+
62+ // Get the original referenced field appearing in the test source (i.e. "f")
63+ final SootField origF = fRef .resolve ();
64+ Assert .assertTrue (!origF .isPhantom ());
65+ Assert .assertEquals ("f" , origF .getName ());
66+
67+ // Change the name of the method so the method reference no
68+ // longer refers to that method.
69+ origF .setName ("newFieldName" );
70+ Assert .assertEquals ("newFieldName" , origF .getName ());
71+
72+ // Changing the field itself does not change the reference
73+ Assert .assertEquals ("f" , fRef .name ());
74+
75+ // There is still just 1 field in the class (but "f" was renamed).
76+ Assert .assertEquals (Arrays .asList ("newFieldName" ),
77+ clas .getFields ().stream ().map (SootField ::getName ).sorted ().collect (Collectors .toList ()));
78+
79+ // When resolving the reference, the cached value is not used since the
80+ // original field was renamed. It now gives a different field (that was
81+ // created automatically since a field with the name "f" no longer exists).
82+ final SootField newF = fRef .resolve ();
83+ Assert .assertNotSame (origF , newF );
84+ Assert .assertEquals ("f" , newF .getName ());
85+
86+ // There are now 2 fields since resolving "f" created it again.
87+ Assert .assertEquals (Arrays .asList ("f" , "newFieldName" ),
88+ clas .getFields ().stream ().map (SootField ::getName ).sorted ().collect (Collectors .toList ()));
89+ }
90+
91+ @ Test
92+ public void testCachingInvalidation_Type () throws Exception {
93+ SootMethod m1 = prepareTarget (methodSigFromComponents (TEST_TARGET_CLASS , "void" , "m1" ), TEST_TARGET_CLASS );
94+ final SootClass clas = m1 .getDeclaringClass ();
95+
96+ // There is only 1 field in the class originally with boolean type.
97+ Assert .assertEquals (Arrays .asList ("f" ),
98+ clas .getFields ().stream ().map (SootField ::getName ).sorted ().collect (Collectors .toList ()));
99+ Assert .assertEquals (Arrays .<Type >asList (BooleanType .v ()),
100+ clas .getFields ().stream ().map (SootField ::getType ).sorted ().collect (Collectors .toList ()));
101+
102+ // Ensure the previous value of AbstractSootFieldRef#resolveCache
103+ // is not used if the referenced field itself is modified.
104+ final Body b = m1 .retrieveActiveBody ();
105+ final SootFieldRef fRef = getFieldRef (b );
106+ Assert .assertEquals ("f" , fRef .name ());
107+ Assert .assertEquals (BooleanType .v (), fRef .type ());
108+
109+ // Get the original referenced field appearing in the test source (i.e. "f")
110+ final SootField origF = fRef .resolve ();
111+ Assert .assertTrue (!origF .isPhantom ());
112+ Assert .assertEquals (BooleanType .v (), origF .getType ());
113+
114+ // Change the type of the method so the method reference no
115+ // longer refers to that method.
116+ origF .setType (IntType .v ());
117+ Assert .assertEquals (IntType .v (), origF .getType ());
118+
119+ // Changing the field itself does not change the reference
120+ Assert .assertEquals (BooleanType .v (), fRef .type ());
121+
122+ // There is still just 1 field in the class (but type of "f" was changed).
123+ Assert .assertEquals (Arrays .asList ("f" ),
124+ clas .getFields ().stream ().map (SootField ::getName ).sorted ().collect (Collectors .toList ()));
125+ Assert .assertEquals (Arrays .<Type >asList (IntType .v ()),
126+ clas .getFields ().stream ().map (SootField ::getType ).sorted ().collect (Collectors .toList ()));
127+
128+ // When resolving the reference, the cached value is not used since
129+ // the original field's type was changed. It now gives 'null'.
130+ final SootField newF = fRef .resolve ();
131+ Assert .assertNotSame (origF , newF );
132+ Assert .assertNull (newF );
133+
134+ // There is still just 1 field in the class (but type of "f" was changed).
135+ Assert .assertEquals (Arrays .asList ("f" ),
136+ clas .getFields ().stream ().map (SootField ::getName ).sorted ().collect (Collectors .toList ()));
137+ Assert .assertEquals (Arrays .<Type >asList (IntType .v ()),
138+ clas .getFields ().stream ().map (SootField ::getType ).sorted ().collect (Collectors .toList ()));
139+ }
140+
141+ private static SootFieldRef getFieldRef (Body b ) {
142+ SootFieldRef retVal = null ;
143+ UnitPatchingChain units = b .getUnits ();
144+ for (Unit u : units ) {
145+ Assert .assertTrue (u instanceof Stmt );
146+ Stmt s = (Stmt ) u ;
147+ if (s .containsFieldRef ()) {
148+ Assert .assertNull (retVal );// the body has exactly 1 FieldRef
149+ retVal = s .getFieldRef ().getFieldRef ();
150+ }
151+ }
152+ Assert .assertNotNull (retVal );// the body has exactly 1 FieldRef
153+ return retVal ;
154+ }
155+ }
0 commit comments