diff --git a/com.ibm.wala.cast/source/java/com/ibm/wala/cast/loader/AstClass.java b/com.ibm.wala.cast/source/java/com/ibm/wala/cast/loader/AstClass.java index 39d440c7e..cf2bad852 100644 --- a/com.ibm.wala.cast/source/java/com/ibm/wala/cast/loader/AstClass.java +++ b/com.ibm.wala.cast/source/java/com/ibm/wala/cast/loader/AstClass.java @@ -153,6 +153,10 @@ abstract public class AstClass implements IClass, ClassConstants { } } + public IField getField(Atom name, TypeName type) { + // assume that for AST classes, you can't have multiple fields with the same name + return getField(name); + } public Collection getDeclaredMethods() { return declaredMethods.values(); } diff --git a/com.ibm.wala.cast/source/java/com/ibm/wala/cast/loader/AstFunctionClass.java b/com.ibm.wala.cast/source/java/com/ibm/wala/cast/loader/AstFunctionClass.java index 5372572ee..713651a9a 100644 --- a/com.ibm.wala.cast/source/java/com/ibm/wala/cast/loader/AstFunctionClass.java +++ b/com.ibm.wala.cast/source/java/com/ibm/wala/cast/loader/AstFunctionClass.java @@ -117,6 +117,11 @@ abstract public class AstFunctionClass implements IClass, ClassConstants { return loader.lookupClass(superReference.getName()).getField(name); } + public IField getField(Atom name, TypeName type) { + // assume that for AST classes, you can't have multiple fields with the same name + return loader.lookupClass(superReference.getName()).getField(name); + } + public TypeReference getReference() { return reference; } diff --git a/com.ibm.wala.core.testdata/build.xml b/com.ibm.wala.core.testdata/build.xml index 9ddeceba7..e7e46c738 100644 --- a/com.ibm.wala.core.testdata/build.xml +++ b/com.ibm.wala.core.testdata/build.xml @@ -152,6 +152,9 @@ + + + diff --git a/com.ibm.wala.core.testdata/classes/DupFieldName.class b/com.ibm.wala.core.testdata/classes/DupFieldName.class new file mode 100644 index 000000000..f0228ec42 Binary files /dev/null and b/com.ibm.wala.core.testdata/classes/DupFieldName.class differ diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/cha/DupFieldsTest.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/cha/DupFieldsTest.java new file mode 100644 index 000000000..e1a3e1f7f --- /dev/null +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/cha/DupFieldsTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package com.ibm.wala.core.tests.cha; + +import java.io.IOException; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.ibm.wala.classLoader.IClass; +import com.ibm.wala.classLoader.IField; +import com.ibm.wala.core.tests.util.TestConstants; +import com.ibm.wala.core.tests.util.WalaTestCase; +import com.ibm.wala.ipa.callgraph.AnalysisScope; +import com.ibm.wala.ipa.cha.ClassHierarchy; +import com.ibm.wala.ipa.cha.ClassHierarchyException; +import com.ibm.wala.types.ClassLoaderReference; +import com.ibm.wala.types.TypeReference; +import com.ibm.wala.util.config.AnalysisScopeReader; +import com.ibm.wala.util.io.FileProvider; +import com.ibm.wala.util.strings.Atom; + +public class DupFieldsTest extends WalaTestCase { + + @Test public void testDupFieldNames() throws IOException, ClassHierarchyException { + AnalysisScope scope = null; + scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, FileProvider.getFile("J2SEClassHierarchyExclusions.txt"), DupFieldsTest.class.getClassLoader()); + ClassHierarchy cha = ClassHierarchy.make(scope); + IClass klass = cha.lookupClass(TypeReference.findOrCreate(ClassLoaderReference.Application, "LDupFieldName")); + boolean threwException = false; + try { + klass.getField(Atom.findOrCreateUnicodeAtom("a")); + } catch (IllegalStateException e) { + threwException = true; + } + Assert.assertTrue(threwException); + IField f = klass.getField(Atom.findOrCreateUnicodeAtom("a"), TypeReference.IntName); + Assert.assertEquals(f.getFieldTypeReference(), TypeReference.Int); + f = klass.getField(Atom.findOrCreateUnicodeAtom("a"), TypeReference.BooleanName); + Assert.assertEquals(f.getFieldTypeReference(), TypeReference.Boolean); + } +} diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/ArrayClass.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/ArrayClass.java index 44c6f34e3..656f20662 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/ArrayClass.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/ArrayClass.java @@ -129,6 +129,10 @@ public class ArrayClass implements IClass, Constants { return getSuperclass().getField(name); } + public IField getField(Atom name, TypeName typeName) { + return getSuperclass().getField(name, typeName); + } + /* * @see com.ibm.wala.classLoader.IClass#getDeclaredMethods() */ diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java index 445809c0a..36d81c985 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java @@ -121,7 +121,7 @@ public abstract class BytecodeClass implements IClass { protected int hashCode; private final HashMap fieldMap = HashMapFactory.make(5); - + /** * A warning for when we get a class not found exception */ @@ -192,12 +192,17 @@ public abstract class BytecodeClass implements IClass { if (fieldMap.containsKey(name)) { return fieldMap.get(name); } else { - IField f = findDeclaredField(name); - if (f != null) { - fieldMap.put(name, f); - return f; + List fields = findDeclaredField(name); + if (!fields.isEmpty()) { + if (fields.size() == 1) { + IField f = fields.iterator().next(); + fieldMap.put(name, f); + return f; + } else { + throw new IllegalStateException("multiple fields with name " + name); + } } else if ((superClass = getSuperclass()) != null) { - f = superClass.getField(name); + IField f = superClass.getField(name); if (f != null) { fieldMap.put(name, f); return f; @@ -205,7 +210,7 @@ public abstract class BytecodeClass implements IClass { } // try superinterfaces for (IClass i : getAllImplementedInterfaces()) { - f = i.getField(name); + IField f = i.getField(name); if (f != null) { fieldMap.put(name, f); return f; @@ -216,6 +221,43 @@ public abstract class BytecodeClass implements IClass { return null; } + + public IField getField(Atom name, TypeName type) { + try { + // typically, there will be at most one field with the name + IField field = getField(name); + if (field != null && field.getFieldTypeReference().getName().equals(type)) { + return field; + } else { + return null; + } + } catch (IllegalStateException e) { + assert e.getMessage().startsWith("multiple fields with"); + // multiple fields. look through all of them and see if any have the appropriate type + List fields = findDeclaredField(name); + for (IField f : fields) { + if (f.getFieldTypeReference().getName().equals(type)) { + return f; + } + } + // check superclass + if (getSuperclass() != null) { + IField f = superClass.getField(name, type); + if (f != null) { + return f; + } + } + // try superinterfaces + for (IClass i : getAllImplementedInterfaces()) { + IField f = i.getField(name, type); + if (f != null) { + return f; + } + } + } + return null; + } + private void computeSuperclass() { superclassComputed = true; @@ -466,11 +508,14 @@ public abstract class BytecodeClass implements IClass { return result; } - protected IField findDeclaredField(Atom name) { + protected List findDeclaredField(Atom name) { + + List result = new ArrayList(1); + if (instanceFields != null) { for (int i = 0; i < instanceFields.length; i++) { if (instanceFields[i].getName() == name) { - return instanceFields[i]; + result.add(instanceFields[i]); } } } @@ -478,12 +523,12 @@ public abstract class BytecodeClass implements IClass { if (staticFields != null) { for (int i = 0; i < staticFields.length; i++) { if (staticFields[i].getName() == name) { - return staticFields[i]; + result.add(staticFields[i]); } } } - return null; + return result; } protected void addFieldToList(List L, Atom name, ImmutableByteArray fieldType, int accessFlags, diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/IClass.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/IClass.java index 9cdd4715f..03fa72f41 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/IClass.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/IClass.java @@ -88,9 +88,16 @@ public interface IClass extends IClassHierarchyDweller { /** * Finds a field. + * + * @throws IllegalStateException if the class contains multiple fields with name name. */ IField getField(Atom name); + /** + * Finds a field, given a name and a type. Returns null if not found. + */ + IField getField(Atom name, TypeName type); + /** * @return canonical TypeReference corresponding to this class */ diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/SyntheticClass.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/SyntheticClass.java index 9476803bd..fa81c54bf 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/SyntheticClass.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/SyntheticClass.java @@ -17,6 +17,7 @@ import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.types.ClassLoaderReference; import com.ibm.wala.types.TypeName; import com.ibm.wala.types.TypeReference; +import com.ibm.wala.util.strings.Atom; /** * An {@link IClass} that exists nowhere in bytecode. @@ -121,4 +122,11 @@ public abstract class SyntheticClass implements IClass { public TypeName getName() { return getReference().getName(); } + + /** + * we assume synthetic classes do not need to have multiple fields with the same name. + */ + public IField getField(Atom name, TypeName typeName) { + return getField(name); + } }