diff --git a/src/main/java/soot/AbstractSootFieldRef.java b/src/main/java/soot/AbstractSootFieldRef.java index 4fbb7dc7946..1cbf8e3898e 100644 --- a/src/main/java/soot/AbstractSootFieldRef.java +++ b/src/main/java/soot/AbstractSootFieldRef.java @@ -34,15 +34,17 @@ * actually exist; the actual target of the reference is determined according to the resolution procedure in the Java Virtual * Machine Specification, 2nd ed, section 5.4.3.2. */ - public class AbstractSootFieldRef implements SootFieldRef { private static final Logger logger = LoggerFactory.getLogger(AbstractSootFieldRef.class); + private final SootClass declaringClass; + private final String name; + private final Type type; + private final boolean isStatic; + + private SootField resolveCache = null; + public AbstractSootFieldRef(SootClass declaringClass, String name, Type type, boolean isStatic) { - this.declaringClass = declaringClass; - this.name = name; - this.type = type; - this.isStatic = isStatic; if (declaringClass == null) { throw new RuntimeException("Attempt to create SootFieldRef with null class"); } @@ -52,13 +54,12 @@ public AbstractSootFieldRef(SootClass declaringClass, String name, Type type, bo if (type == null) { throw new RuntimeException("Attempt to create SootFieldRef with null type"); } + this.declaringClass = declaringClass; + this.name = name; + this.type = type; + this.isStatic = isStatic; } - private final SootClass declaringClass; - private final String name; - private final Type type; - private final boolean isStatic; - @Override public SootClass declaringClass() { return declaringClass; @@ -85,9 +86,6 @@ public String getSignature() { } public class FieldResolutionFailedException extends ResolutionFailedException { - /** - * - */ private static final long serialVersionUID = -4657113720516199499L; public FieldResolutionFailedException() { @@ -97,8 +95,7 @@ public FieldResolutionFailedException() { @Override public String toString() { - StringBuffer ret = new StringBuffer(); - ret.append(super.toString()); + StringBuilder ret = new StringBuilder(super.toString()); resolve(ret); return ret.toString(); } @@ -106,33 +103,38 @@ public String toString() { @Override public SootField resolve() { - return resolve(null); + SootField cached = this.resolveCache; + // Use the cached SootField if available and still valid + if (cached == null || !cached.isValidResolve(this)) { + cached = resolve(null); + this.resolveCache = cached; + } + return cached; } private SootField checkStatic(SootField ret) { if ((Options.v().wrong_staticness() == Options.wrong_staticness_fail - || Options.v().wrong_staticness() == Options.wrong_staticness_fixstrict) - && ret.isStatic() != isStatic() && !ret.isPhantom()) { + || Options.v().wrong_staticness() == Options.wrong_staticness_fixstrict) && ret.isStatic() != isStatic() + && !ret.isPhantom()) { throw new ResolutionFailedException("Resolved " + this + " to " + ret + " which has wrong static-ness"); } return ret; } - private SootField resolve(StringBuffer trace) { + private SootField resolve(StringBuilder trace) { SootClass cl = declaringClass; while (true) { if (trace != null) { - trace.append("Looking in " + cl + " which has fields " + cl.getFields() + "\n"); + trace.append("Looking in ").append(cl).append(" which has fields ").append(cl.getFields()).append('\n'); } // Check whether we have the field in the current class SootField clField = cl.getFieldUnsafe(name, type); if (clField != null) { return checkStatic(clField); - } - // If we have a phantom class, we directly construct a phantom field - // in it and don't care about superclasses. - else if (Scene.v().allowsPhantomRefs() && cl.isPhantom()) { + } else if (Scene.v().allowsPhantomRefs() && cl.isPhantom()) { + // If we have a phantom class, we directly construct a phantom field + // in it and don't care about superclasses. synchronized (cl) { // Check that no other thread has created the field in the // meantime @@ -164,7 +166,7 @@ else if (Scene.v().allowsPhantomRefs() && cl.isPhantom()) { } if (trace != null) { - trace.append("Looking in " + iface + " which has fields " + iface.getFields() + "\n"); + trace.append("Looking in ").append(iface).append(" which has fields ").append(iface.getFields()).append('\n'); } SootField ifaceField = iface.getFieldUnsafe(name, type); if (ifaceField != null) { @@ -208,7 +210,7 @@ else if (Scene.v().allowsPhantomRefs() && cl.isPhantom()) { if (trace == null) { FieldResolutionFailedException e = new FieldResolutionFailedException(); if (Options.v().ignore_resolution_errors()) { - logger.debug("" + e.getMessage()); + logger.debug(e.getMessage()); } else { throw e; } @@ -250,38 +252,34 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - if (obj == null) { + if (obj == null || this.getClass() != obj.getClass()) { return false; } - if (getClass() != obj.getClass()) { + AbstractSootFieldRef other = (AbstractSootFieldRef) obj; + if (this.isStatic != other.isStatic) { return false; } - AbstractSootFieldRef other = (AbstractSootFieldRef) obj; - if (declaringClass == null) { + if (this.declaringClass == null) { if (other.declaringClass != null) { return false; } - } else if (!declaringClass.equals(other.declaringClass)) { - return false; - } - if (isStatic != other.isStatic) { + } else if (!this.declaringClass.equals(other.declaringClass)) { return false; } - if (name == null) { + if (this.name == null) { if (other.name != null) { return false; } - } else if (!name.equals(other.name)) { + } else if (!this.name.equals(other.name)) { return false; } - if (type == null) { + if (this.type == null) { if (other.type != null) { return false; } - } else if (!type.equals(other.type)) { + } else if (!this.type.equals(other.type)) { return false; } return true; } - } diff --git a/src/main/java/soot/SootField.java b/src/main/java/soot/SootField.java index a4948c7685a..83200f3224a 100644 --- a/src/main/java/soot/SootField.java +++ b/src/main/java/soot/SootField.java @@ -23,6 +23,7 @@ * #L% */ +import java.util.Objects; import soot.jimple.paddle.PaddleField; import soot.jimple.spark.pag.SparkField; import soot.options.Options; @@ -33,6 +34,7 @@ * Soot representation of a Java field. Can be declared to belong to a SootClass. */ public class SootField extends AbstractHost implements ClassMember, SparkField, Numberable, PaddleField { + protected String name; protected Type type; protected int modifiers; @@ -255,4 +257,9 @@ public final void setNumber(int number) { public SootFieldRef makeRef() { return Scene.v().makeFieldRef(declaringClass, name, type, isStatic()); } + + public boolean isValidResolve(SootFieldRef f) { + return (this.isStatic() == f.isStatic()) && Objects.equals(this.getDeclaringClass(), f.declaringClass()) + && Objects.equals(this.getName(), f.name()) && Objects.equals(this.getType(), f.type()); + } } diff --git a/src/main/java/soot/dava/internal/javaRep/DInstanceFieldRef.java b/src/main/java/soot/dava/internal/javaRep/DInstanceFieldRef.java index 4f67d0186c1..ac57c6b30b0 100644 --- a/src/main/java/soot/dava/internal/javaRep/DInstanceFieldRef.java +++ b/src/main/java/soot/dava/internal/javaRep/DInstanceFieldRef.java @@ -23,7 +23,7 @@ * #L% */ -import java.util.HashSet; +import java.util.Set; import soot.SootFieldRef; import soot.UnitPrinter; @@ -31,31 +31,35 @@ import soot.grimp.internal.GInstanceFieldRef; public class DInstanceFieldRef extends GInstanceFieldRef { - private HashSet thisLocals; - public DInstanceFieldRef(Value base, SootFieldRef fieldRef, HashSet thisLocals) { + private final Set thisLocals; + + public DInstanceFieldRef(Value base, SootFieldRef fieldRef, Set thisLocals) { super(base, fieldRef); this.thisLocals = thisLocals; } + @Override public void toString(UnitPrinter up) { if (thisLocals.contains(getBase())) { - up.fieldRef(fieldRef); + up.fieldRef(getFieldRef()); } else { super.toString(up); } } + @Override public String toString() { if (thisLocals.contains(getBase())) { - return fieldRef.name(); + return getFieldRef().name(); + } else { + return super.toString(); } - - return super.toString(); } + @Override public Object clone() { - return new DInstanceFieldRef(getBase(), fieldRef, thisLocals); + return new DInstanceFieldRef(getBase(), getFieldRef(), thisLocals); } } diff --git a/src/main/java/soot/dava/internal/javaRep/DStaticFieldRef.java b/src/main/java/soot/dava/internal/javaRep/DStaticFieldRef.java index 7d4881e2302..f908744d97c 100644 --- a/src/main/java/soot/dava/internal/javaRep/DStaticFieldRef.java +++ b/src/main/java/soot/dava/internal/javaRep/DStaticFieldRef.java @@ -28,19 +28,11 @@ import soot.jimple.StaticFieldRef; public class DStaticFieldRef extends StaticFieldRef { - private boolean supressDeclaringClass; - public void toString(UnitPrinter up) { - if (!supressDeclaringClass) { - up.type(fieldRef.declaringClass().getType()); - up.literal("."); - } - up.fieldRef(fieldRef); - } + private final boolean supressDeclaringClass; public DStaticFieldRef(SootFieldRef fieldRef, String myClassName) { - super(fieldRef); - supressDeclaringClass = myClassName.equals(fieldRef.declaringClass().getName()); + this(fieldRef, myClassName.equals(fieldRef.declaringClass().getName())); } public DStaticFieldRef(SootFieldRef fieldRef, boolean supressDeclaringClass) { @@ -48,7 +40,18 @@ public DStaticFieldRef(SootFieldRef fieldRef, boolean supressDeclaringClass) { this.supressDeclaringClass = supressDeclaringClass; } + @Override public Object clone() { - return new DStaticFieldRef(fieldRef, supressDeclaringClass); + return new DStaticFieldRef(getFieldRef(), supressDeclaringClass); + } + + @Override + public void toString(UnitPrinter up) { + SootFieldRef fRef = getFieldRef(); + if (!supressDeclaringClass) { + up.type(fRef.declaringClass().getType()); + up.literal("."); + } + up.fieldRef(fRef); } } diff --git a/src/main/java/soot/grimp/internal/GInstanceFieldRef.java b/src/main/java/soot/grimp/internal/GInstanceFieldRef.java index db2d6260182..f10e1482c67 100644 --- a/src/main/java/soot/grimp/internal/GInstanceFieldRef.java +++ b/src/main/java/soot/grimp/internal/GInstanceFieldRef.java @@ -30,29 +30,31 @@ import soot.jimple.internal.AbstractInstanceFieldRef; public class GInstanceFieldRef extends AbstractInstanceFieldRef implements Precedence { + public GInstanceFieldRef(Value base, SootFieldRef fieldRef) { super(Grimp.v().newObjExprBox(base), fieldRef); } - private String toString(Value op, String opString, String rightString) { - String leftOp = opString; - + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + final Value op = getBase(); if (op instanceof Precedence && ((Precedence) op).getPrecedence() < getPrecedence()) { - leftOp = "(" + leftOp + ")"; + sb.append('(').append(op.toString()).append(')'); + } else { + sb.append(op.toString()); } - return leftOp + rightString; - } - - public String toString() { - return toString(getBase(), getBase().toString(), "." + fieldRef.getSignature()); + sb.append('.').append(getFieldRef().getSignature()); + return sb.toString(); } + @Override public int getPrecedence() { return 950; } + @Override public Object clone() { - return new GInstanceFieldRef(Grimp.cloneIfNecessary(getBase()), fieldRef); + return new GInstanceFieldRef(Grimp.cloneIfNecessary(getBase()), getFieldRef()); } - } diff --git a/src/main/java/soot/jimple/StaticFieldRef.java b/src/main/java/soot/jimple/StaticFieldRef.java index 77dd45c8782..ba8d1908f33 100644 --- a/src/main/java/soot/jimple/StaticFieldRef.java +++ b/src/main/java/soot/jimple/StaticFieldRef.java @@ -46,26 +46,32 @@ protected StaticFieldRef(SootFieldRef fieldRef) { this.fieldRef = fieldRef; } + @Override public Object clone() { return new StaticFieldRef(fieldRef); } + @Override public String toString() { return fieldRef.getSignature(); } + @Override public void toString(UnitPrinter up) { up.fieldRef(fieldRef); } + @Override public SootFieldRef getFieldRef() { return fieldRef; } + @Override public void setFieldRef(SootFieldRef fieldRef) { this.fieldRef = fieldRef; } + @Override public SootField getField() { return fieldRef.resolve(); } @@ -75,26 +81,31 @@ public List getUseBoxes() { return Collections.emptyList(); } + @Override public Type getType() { return fieldRef.type(); } + @Override public void apply(Switch sw) { ((RefSwitch) sw).caseStaticFieldRef(this); } + @Override public boolean equivTo(Object o) { if (o instanceof StaticFieldRef) { return ((StaticFieldRef) o).getField().equals(getField()); + } else { + return false; } - - return false; } + @Override public int equivHashCode() { return getField().equivHashCode(); } + @Override public void convertToBaf(JimpleToBafContext context, List out) { Unit u = Baf.v().newStaticGetInst(fieldRef); u.addAllTagsOf(context.getCurrentUnit()); diff --git a/src/main/java/soot/jimple/internal/AbstractInstanceFieldRef.java b/src/main/java/soot/jimple/internal/AbstractInstanceFieldRef.java index 821629967cc..35e147aa417 100644 --- a/src/main/java/soot/jimple/internal/AbstractInstanceFieldRef.java +++ b/src/main/java/soot/jimple/internal/AbstractInstanceFieldRef.java @@ -43,8 +43,9 @@ @SuppressWarnings("serial") public abstract class AbstractInstanceFieldRef implements InstanceFieldRef, ConvertToBaf { + + protected final ValueBox baseBox; protected SootFieldRef fieldRef; - final ValueBox baseBox; protected AbstractInstanceFieldRef(ValueBox baseBox, SootFieldRef fieldRef) { if (fieldRef.isStatic()) { @@ -54,84 +55,95 @@ protected AbstractInstanceFieldRef(ValueBox baseBox, SootFieldRef fieldRef) { this.fieldRef = fieldRef; } + @Override public abstract Object clone(); + @Override public String toString() { return baseBox.getValue().toString() + "." + fieldRef.getSignature(); } + @Override public void toString(UnitPrinter up) { - if (PrecedenceTest.needsBrackets(baseBox, this)) { + final boolean needsBrackets = PrecedenceTest.needsBrackets(baseBox, this); + if (needsBrackets) { up.literal("("); } baseBox.toString(up); - if (PrecedenceTest.needsBrackets(baseBox, this)) { + if (needsBrackets) { up.literal(")"); } up.literal("."); up.fieldRef(fieldRef); } + @Override public Value getBase() { return baseBox.getValue(); } + @Override public ValueBox getBaseBox() { return baseBox; } + @Override public void setBase(Value base) { baseBox.setValue(base); } + @Override public SootFieldRef getFieldRef() { return fieldRef; } + @Override public void setFieldRef(SootFieldRef fieldRef) { this.fieldRef = fieldRef; } + @Override public SootField getField() { return fieldRef.resolve(); } @Override public final List getUseBoxes() { - List useBoxes = new ArrayList(); - - useBoxes.addAll(baseBox.getValue().getUseBoxes()); + List useBoxes = new ArrayList(baseBox.getValue().getUseBoxes()); useBoxes.add(baseBox); - return useBoxes; } + @Override public Type getType() { return fieldRef.type(); } + @Override public void apply(Switch sw) { ((RefSwitch) sw).caseInstanceFieldRef(this); } + @Override public boolean equivTo(Object o) { if (o instanceof AbstractInstanceFieldRef) { AbstractInstanceFieldRef fr = (AbstractInstanceFieldRef) o; - return fr.getField().equals(getField()) && fr.baseBox.getValue().equivTo(baseBox.getValue()); + return fr.getField().equals(this.getField()) && fr.baseBox.getValue().equivTo(this.baseBox.getValue()); } return false; } /** Returns a hash code for this object, consistent with structural equality. */ + @Override public int equivHashCode() { return getField().equivHashCode() * 101 + baseBox.getValue().equivHashCode() + 17; } + @Override public void convertToBaf(JimpleToBafContext context, List out) { ((ConvertToBaf) getBase()).convertToBaf(context, out); - Unit u; - out.add(u = Baf.v().newFieldGetInst(fieldRef)); - + Unit u = Baf.v().newFieldGetInst(fieldRef); + out.add(u); u.addAllTagsOf(context.getCurrentUnit()); } } diff --git a/src/main/java/soot/jimple/internal/JInstanceFieldRef.java b/src/main/java/soot/jimple/internal/JInstanceFieldRef.java index fc9e665ea70..0231de5f01e 100644 --- a/src/main/java/soot/jimple/internal/JInstanceFieldRef.java +++ b/src/main/java/soot/jimple/internal/JInstanceFieldRef.java @@ -28,11 +28,13 @@ import soot.jimple.Jimple; public class JInstanceFieldRef extends AbstractInstanceFieldRef { + public JInstanceFieldRef(Value base, SootFieldRef fieldRef) { super(Jimple.v().newLocalBox(base), fieldRef); } + @Override public Object clone() { - return new JInstanceFieldRef(Jimple.cloneIfNecessary(getBase()), fieldRef); + return new JInstanceFieldRef(Jimple.cloneIfNecessary(getBase()), getFieldRef()); } } diff --git a/src/systemTest/java/soot/AbstractSootFieldRefTest.java b/src/systemTest/java/soot/AbstractSootFieldRefTest.java new file mode 100644 index 00000000000..54c0d467128 --- /dev/null +++ b/src/systemTest/java/soot/AbstractSootFieldRefTest.java @@ -0,0 +1,154 @@ +package soot; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2021 Timothy Hoffman + * %% + * 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 java.util.Arrays; +import java.util.stream.Collectors; + +import org.junit.Assert; +import org.junit.Test; +import org.powermock.core.classloader.annotations.PowerMockIgnore; + +import soot.jimple.Stmt; +import soot.testing.framework.AbstractTestingFramework; + +/** + * @author Timothy Hoffman + */ +@PowerMockIgnore({ "com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.*" }) +public class AbstractSootFieldRefTest extends AbstractTestingFramework { + + private static final String TEST_TARGET_CLASS = "soot.AbstractSootFieldRefTestInput"; + + @Override + protected void setupSoot() { + } + + @Test + public void testCachingInvalidation_Name() throws Exception { + SootMethod m1 = prepareTarget(methodSigFromComponents(TEST_TARGET_CLASS, "void", "m1"), TEST_TARGET_CLASS); + final SootClass clas = m1.getDeclaringClass(); + + // There is only 1 field in the class originally. + Assert.assertEquals(Arrays.asList("f"), + clas.getFields().stream().map(SootField::getName).sorted().collect(Collectors.toList())); + + // Ensure the previous value of AbstractSootFieldRef#resolveCache + // is not used if the referenced field itself is modified. + final Body b = m1.retrieveActiveBody(); + final SootFieldRef fRef = getFieldRef(b); + Assert.assertEquals("f", fRef.name()); + + // Get the original referenced field appearing in the test source (i.e. "f") + final SootField origF = fRef.resolve(); + Assert.assertTrue(!origF.isPhantom()); + Assert.assertEquals("f", origF.getName()); + + // Change the name of the method so the method reference no + // longer refers to that method. + origF.setName("newFieldName"); + Assert.assertEquals("newFieldName", origF.getName()); + + // Changing the field itself does not change the reference + Assert.assertEquals("f", fRef.name()); + + // There is still just 1 field in the class (but "f" was renamed). + Assert.assertEquals(Arrays.asList("newFieldName"), + clas.getFields().stream().map(SootField::getName).sorted().collect(Collectors.toList())); + + // When resolving the reference, the cached value is not used since the + // original field was renamed. It now gives a different field (that was + // created automatically since a field with the name "f" no longer exists). + final SootField newF = fRef.resolve(); + Assert.assertNotSame(origF, newF); + Assert.assertEquals("f", newF.getName()); + + // There are now 2 fields since resolving "f" created it again. + Assert.assertEquals(Arrays.asList("f", "newFieldName"), + clas.getFields().stream().map(SootField::getName).sorted().collect(Collectors.toList())); + } + + @Test + public void testCachingInvalidation_Type() throws Exception { + SootMethod m1 = prepareTarget(methodSigFromComponents(TEST_TARGET_CLASS, "void", "m1"), TEST_TARGET_CLASS); + final SootClass clas = m1.getDeclaringClass(); + + // There is only 1 field in the class originally with boolean type. + Assert.assertEquals(Arrays.asList("f"), + clas.getFields().stream().map(SootField::getName).sorted().collect(Collectors.toList())); + Assert.assertEquals(Arrays.asList(BooleanType.v()), + clas.getFields().stream().map(SootField::getType).sorted().collect(Collectors.toList())); + + // Ensure the previous value of AbstractSootFieldRef#resolveCache + // is not used if the referenced field itself is modified. + final Body b = m1.retrieveActiveBody(); + final SootFieldRef fRef = getFieldRef(b); + Assert.assertEquals("f", fRef.name()); + Assert.assertEquals(BooleanType.v(), fRef.type()); + + // Get the original referenced field appearing in the test source (i.e. "f") + final SootField origF = fRef.resolve(); + Assert.assertTrue(!origF.isPhantom()); + Assert.assertEquals(BooleanType.v(), origF.getType()); + + // Change the type of the method so the method reference no + // longer refers to that method. + origF.setType(IntType.v()); + Assert.assertEquals(IntType.v(), origF.getType()); + + // Changing the field itself does not change the reference + Assert.assertEquals(BooleanType.v(), fRef.type()); + + // There is still just 1 field in the class (but type of "f" was changed). + Assert.assertEquals(Arrays.asList("f"), + clas.getFields().stream().map(SootField::getName).sorted().collect(Collectors.toList())); + Assert.assertEquals(Arrays.asList(IntType.v()), + clas.getFields().stream().map(SootField::getType).sorted().collect(Collectors.toList())); + + // When resolving the reference, the cached value is not used since + // the original field's type was changed. It now gives 'null'. + final SootField newF = fRef.resolve(); + Assert.assertNotSame(origF, newF); + Assert.assertNull(newF); + + // There is still just 1 field in the class (but type of "f" was changed). + Assert.assertEquals(Arrays.asList("f"), + clas.getFields().stream().map(SootField::getName).sorted().collect(Collectors.toList())); + Assert.assertEquals(Arrays.asList(IntType.v()), + clas.getFields().stream().map(SootField::getType).sorted().collect(Collectors.toList())); + } + + private static SootFieldRef getFieldRef(Body b) { + SootFieldRef retVal = null; + for (Unit u : b.getUnits()) { + Assert.assertTrue(u instanceof Stmt); + Stmt s = (Stmt) u; + if (s.containsFieldRef()) { + Assert.assertNull(retVal);// the body has exactly 1 FieldRef + retVal = s.getFieldRef().getFieldRef(); + } + } + Assert.assertNotNull(retVal);// the body has exactly 1 FieldRef + return retVal; + } +} diff --git a/src/systemTest/targets/soot/AbstractSootFieldRefTestInput.java b/src/systemTest/targets/soot/AbstractSootFieldRefTestInput.java new file mode 100644 index 00000000000..f01fed02c4e --- /dev/null +++ b/src/systemTest/targets/soot/AbstractSootFieldRefTestInput.java @@ -0,0 +1,35 @@ +package soot; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2021 Timothy Hoffman + * %% + * 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% + */ + +/** + * @author Timothy Hoffman + */ +public class AbstractSootFieldRefTestInput { + + public boolean f; + + public void m1() { + this.f = true; + } +}