diff options
| author | 2014-09-14 19:06:55 -0400 | |
|---|---|---|
| committer | 2014-09-14 19:06:55 -0400 | |
| commit | aa68099bb252dd1a1c275459f8babe537868bcaf (patch) | |
| tree | 88e600c5d3aec3e0d06558a8b7615a17cb80b34f | |
| parent | added some tests for a small inheritance hierarchy (diff) | |
| download | enigma-aa68099bb252dd1a1c275459f8babe537868bcaf.tar.gz enigma-aa68099bb252dd1a1c275459f8babe537868bcaf.tar.xz enigma-aa68099bb252dd1a1c275459f8babe537868bcaf.zip | |
fixed bug with method references pointing to wrong class
6 files changed, 107 insertions, 41 deletions
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index b4096e9d..f4843164 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java | |||
| @@ -127,7 +127,7 @@ public class JarIndex | |||
| 127 | } | 127 | } |
| 128 | for( CtField field : c.getDeclaredFields() ) | 128 | for( CtField field : c.getDeclaredFields() ) |
| 129 | { | 129 | { |
| 130 | indexField( field ); | 130 | m_translationIndex.addField( className, field.getName() ); |
| 131 | } | 131 | } |
| 132 | for( CtBehavior behavior : c.getDeclaredBehaviors() ) | 132 | for( CtBehavior behavior : c.getDeclaredBehaviors() ) |
| 133 | { | 133 | { |
| @@ -135,9 +135,19 @@ public class JarIndex | |||
| 135 | } | 135 | } |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | // step 4: index field, method, constructor references | ||
| 139 | for( CtClass c : JarClassIterator.classes( jar ) ) | ||
| 140 | { | ||
| 141 | ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); | ||
| 142 | for( CtBehavior behavior : c.getDeclaredBehaviors() ) | ||
| 143 | { | ||
| 144 | indexBehaviorReferences( behavior ); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 138 | if( buildInnerClasses ) | 148 | if( buildInnerClasses ) |
| 139 | { | 149 | { |
| 140 | // step 4: index inner classes and anonymous classes | 150 | // step 5: index inner classes and anonymous classes |
| 141 | for( CtClass c : JarClassIterator.classes( jar ) ) | 151 | for( CtClass c : JarClassIterator.classes( jar ) ) |
| 142 | { | 152 | { |
| 143 | ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); | 153 | ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); |
| @@ -163,7 +173,7 @@ public class JarIndex | |||
| 163 | } | 173 | } |
| 164 | } | 174 | } |
| 165 | 175 | ||
| 166 | // step 5: update other indices with inner class info | 176 | // step 6: update other indices with inner class info |
| 167 | Map<String,String> renames = Maps.newHashMap(); | 177 | Map<String,String> renames = Maps.newHashMap(); |
| 168 | for( Map.Entry<String,String> entry : m_outerClasses.entrySet() ) | 178 | for( Map.Entry<String,String> entry : m_outerClasses.entrySet() ) |
| 169 | { | 179 | { |
| @@ -183,25 +193,14 @@ public class JarIndex | |||
| 183 | EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences ); | 193 | EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences ); |
| 184 | } | 194 | } |
| 185 | 195 | ||
| 186 | private void indexField( CtField field ) | ||
| 187 | { | ||
| 188 | String className = Descriptor.toJvmName( field.getDeclaringClass().getName() ); | ||
| 189 | m_translationIndex.addField( className, field.getName() ); | ||
| 190 | } | ||
| 191 | |||
| 192 | private void indexBehavior( CtBehavior behavior ) | 196 | private void indexBehavior( CtBehavior behavior ) |
| 193 | { | 197 | { |
| 194 | // get the method entry | 198 | // get the behavior entry |
| 195 | String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); | 199 | String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); |
| 196 | final BehaviorEntry thisEntry; | 200 | final BehaviorEntry behaviorEntry = getBehaviorEntry( behavior ); |
| 197 | if( behavior instanceof CtMethod ) | 201 | if( behaviorEntry instanceof MethodEntry ) |
| 198 | { | 202 | { |
| 199 | MethodEntry methodEntry = new MethodEntry( | 203 | MethodEntry methodEntry = (MethodEntry)behaviorEntry; |
| 200 | new ClassEntry( className ), | ||
| 201 | behavior.getName(), | ||
| 202 | behavior.getSignature() | ||
| 203 | ); | ||
| 204 | thisEntry = methodEntry; | ||
| 205 | 204 | ||
| 206 | // index implementation | 205 | // index implementation |
| 207 | m_methodImplementations.put( className, methodEntry ); | 206 | m_methodImplementations.put( className, methodEntry ); |
| @@ -218,24 +217,13 @@ public class JarIndex | |||
| 218 | m_bridgeMethods.put( bridgedMethodEntry, methodEntry ); | 217 | m_bridgeMethods.put( bridgedMethodEntry, methodEntry ); |
| 219 | } | 218 | } |
| 220 | } | 219 | } |
| 221 | else if( behavior instanceof CtConstructor ) | 220 | // looks like we don't care about constructors here |
| 222 | { | 221 | } |
| 223 | boolean isStatic = behavior.getName().equals( "<clinit>" ); | 222 | |
| 224 | if( isStatic ) | 223 | private void indexBehaviorReferences( CtBehavior behavior ) |
| 225 | { | 224 | { |
| 226 | thisEntry = new ConstructorEntry( new ClassEntry( className ) ); | ||
| 227 | } | ||
| 228 | else | ||
| 229 | { | ||
| 230 | thisEntry = new ConstructorEntry( new ClassEntry( className ), behavior.getSignature() ); | ||
| 231 | } | ||
| 232 | } | ||
| 233 | else | ||
| 234 | { | ||
| 235 | throw new IllegalArgumentException( "behavior must be a method or a constructor!" ); | ||
| 236 | } | ||
| 237 | |||
| 238 | // index method calls | 225 | // index method calls |
| 226 | final BehaviorEntry behaviorEntry = getBehaviorEntry( behavior ); | ||
| 239 | try | 227 | try |
| 240 | { | 228 | { |
| 241 | behavior.instrument( new ExprEditor( ) | 229 | behavior.instrument( new ExprEditor( ) |
| @@ -249,9 +237,10 @@ public class JarIndex | |||
| 249 | call.getMethodName(), | 237 | call.getMethodName(), |
| 250 | call.getSignature() | 238 | call.getSignature() |
| 251 | ); | 239 | ); |
| 240 | calledMethodEntry = resolveMethodClass( calledMethodEntry ); | ||
| 252 | EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( | 241 | EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( |
| 253 | calledMethodEntry, | 242 | calledMethodEntry, |
| 254 | thisEntry | 243 | behaviorEntry |
| 255 | ); | 244 | ); |
| 256 | m_behaviorReferences.put( calledMethodEntry, reference ); | 245 | m_behaviorReferences.put( calledMethodEntry, reference ); |
| 257 | } | 246 | } |
| @@ -266,7 +255,7 @@ public class JarIndex | |||
| 266 | ); | 255 | ); |
| 267 | EntryReference<FieldEntry,BehaviorEntry> reference = new EntryReference<FieldEntry,BehaviorEntry>( | 256 | EntryReference<FieldEntry,BehaviorEntry> reference = new EntryReference<FieldEntry,BehaviorEntry>( |
| 268 | calledFieldEntry, | 257 | calledFieldEntry, |
| 269 | thisEntry | 258 | behaviorEntry |
| 270 | ); | 259 | ); |
| 271 | m_fieldReferences.put( calledFieldEntry, reference ); | 260 | m_fieldReferences.put( calledFieldEntry, reference ); |
| 272 | } | 261 | } |
| @@ -284,7 +273,7 @@ public class JarIndex | |||
| 284 | ); | 273 | ); |
| 285 | EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( | 274 | EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( |
| 286 | calledConstructorEntry, | 275 | calledConstructorEntry, |
| 287 | thisEntry | 276 | behaviorEntry |
| 288 | ); | 277 | ); |
| 289 | m_behaviorReferences.put( calledConstructorEntry, reference ); | 278 | m_behaviorReferences.put( calledConstructorEntry, reference ); |
| 290 | } | 279 | } |
| @@ -299,7 +288,7 @@ public class JarIndex | |||
| 299 | ); | 288 | ); |
| 300 | EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( | 289 | EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( |
| 301 | calledConstructorEntry, | 290 | calledConstructorEntry, |
| 302 | thisEntry | 291 | behaviorEntry |
| 303 | ); | 292 | ); |
| 304 | m_behaviorReferences.put( calledConstructorEntry, reference ); | 293 | m_behaviorReferences.put( calledConstructorEntry, reference ); |
| 305 | } | 294 | } |
| @@ -311,6 +300,59 @@ public class JarIndex | |||
| 311 | } | 300 | } |
| 312 | } | 301 | } |
| 313 | 302 | ||
| 303 | private BehaviorEntry getBehaviorEntry( CtBehavior behavior ) | ||
| 304 | { | ||
| 305 | String className = Descriptor.toJvmName( behavior.getDeclaringClass().getName() ); | ||
| 306 | if( behavior instanceof CtMethod ) | ||
| 307 | { | ||
| 308 | return new MethodEntry( | ||
| 309 | new ClassEntry( className ), | ||
| 310 | behavior.getName(), | ||
| 311 | behavior.getSignature() | ||
| 312 | ); | ||
| 313 | } | ||
| 314 | else if( behavior instanceof CtConstructor ) | ||
| 315 | { | ||
| 316 | boolean isStatic = behavior.getName().equals( "<clinit>" ); | ||
| 317 | if( isStatic ) | ||
| 318 | { | ||
| 319 | return new ConstructorEntry( new ClassEntry( className ) ); | ||
| 320 | } | ||
| 321 | else | ||
| 322 | { | ||
| 323 | return new ConstructorEntry( new ClassEntry( className ), behavior.getSignature() ); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | else | ||
| 327 | { | ||
| 328 | throw new IllegalArgumentException( "behavior must be a method or a constructor!" ); | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | private MethodEntry resolveMethodClass( MethodEntry methodEntry ) | ||
| 333 | { | ||
| 334 | // this entry could refer to a method on a class where the method is not actually implemented | ||
| 335 | // travel up the inheritance tree to find the closest implementation | ||
| 336 | while( !isMethodImplemented( methodEntry ) ) | ||
| 337 | { | ||
| 338 | // is there a parent class? | ||
| 339 | String superclassName = m_translationIndex.getSuperclassName( methodEntry.getClassName() ); | ||
| 340 | if( superclassName == null ) | ||
| 341 | { | ||
| 342 | // this is probably a method from a class in a library | ||
| 343 | // we can't trace the implementation up any higher unless we index the library | ||
| 344 | return methodEntry; | ||
| 345 | } | ||
| 346 | |||
| 347 | // move up to the parent class | ||
| 348 | methodEntry = new MethodEntry( | ||
| 349 | new ClassEntry( superclassName ), | ||
| 350 | methodEntry.getName(), | ||
| 351 | methodEntry.getSignature() | ||
| 352 | ); | ||
| 353 | } | ||
| 354 | return methodEntry; | ||
| 355 | } | ||
| 314 | 356 | ||
| 315 | private CtMethod getBridgedMethod( CtMethod method ) | 357 | private CtMethod getBridgedMethod( CtMethod method ) |
| 316 | { | 358 | { |
diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 5ded5df0..3d488ee4 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java | |||
| @@ -220,7 +220,8 @@ public class TestJarIndexInheritanceTree | |||
| 220 | source = new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ); | 220 | source = new MethodEntry( m_baseClass, "a", "()Ljava/lang/String;" ); |
| 221 | references = m_index.getBehaviorReferences( source ); | 221 | references = m_index.getBehaviorReferences( source ); |
| 222 | assertThat( references, containsInAnyOrder( | 222 | assertThat( references, containsInAnyOrder( |
| 223 | newBehaviorReferenceByMethod( source, m_subClassAA.getName(), "a", "()Ljava/lang/String;" ) | 223 | newBehaviorReferenceByMethod( source, m_subClassAA.getName(), "a", "()Ljava/lang/String;" ), |
| 224 | newBehaviorReferenceByMethod( source, m_subClassB.getName(), "a", "()V" ) | ||
| 224 | ) ); | 225 | ) ); |
| 225 | 226 | ||
| 226 | // subclassAA.getName() | 227 | // subclassAA.getName() |
diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java index a6b38454..8402dde3 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java | |||
| @@ -1,18 +1,23 @@ | |||
| 1 | package cuchaz.enigma.inputs.inheritanceTree; | 1 | package cuchaz.enigma.inputs.inheritanceTree; |
| 2 | 2 | ||
| 3 | // none/a | ||
| 3 | public abstract class BaseClass | 4 | public abstract class BaseClass |
| 4 | { | 5 | { |
| 6 | // a | ||
| 5 | private String m_name; | 7 | private String m_name; |
| 6 | 8 | ||
| 9 | // <init>(Ljava/lang/String;)V | ||
| 7 | protected BaseClass( String name ) | 10 | protected BaseClass( String name ) |
| 8 | { | 11 | { |
| 9 | m_name = name; | 12 | m_name = name; |
| 10 | } | 13 | } |
| 11 | 14 | ||
| 15 | // a()Ljava/lang/String; | ||
| 12 | public String getName( ) | 16 | public String getName( ) |
| 13 | { | 17 | { |
| 14 | return m_name; | 18 | return m_name; |
| 15 | } | 19 | } |
| 16 | 20 | ||
| 21 | // a()V | ||
| 17 | public abstract void doBaseThings( ); | 22 | public abstract void doBaseThings( ); |
| 18 | } | 23 | } |
diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java index f4780a26..ed507093 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java | |||
| @@ -1,9 +1,12 @@ | |||
| 1 | package cuchaz.enigma.inputs.inheritanceTree; | 1 | package cuchaz.enigma.inputs.inheritanceTree; |
| 2 | 2 | ||
| 3 | // none/b extends none/a | ||
| 3 | public abstract class SubclassA extends BaseClass | 4 | public abstract class SubclassA extends BaseClass |
| 4 | { | 5 | { |
| 6 | // <init>(Ljava/lang/String;)V | ||
| 5 | protected SubclassA( String name ) | 7 | protected SubclassA( String name ) |
| 6 | { | 8 | { |
| 9 | // call to none/a.<init>(Ljava/lang/String)V | ||
| 7 | super( name ); | 10 | super( name ); |
| 8 | } | 11 | } |
| 9 | } | 12 | } |
diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java index 4001e7a3..fc4c8eed 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java | |||
| @@ -1,24 +1,33 @@ | |||
| 1 | package cuchaz.enigma.inputs.inheritanceTree; | 1 | package cuchaz.enigma.inputs.inheritanceTree; |
| 2 | 2 | ||
| 3 | // none/c extends none/a | ||
| 3 | public class SubclassB extends BaseClass | 4 | public class SubclassB extends BaseClass |
| 4 | { | 5 | { |
| 6 | // a | ||
| 5 | private int m_numThings; | 7 | private int m_numThings; |
| 6 | 8 | ||
| 9 | // <init>()V | ||
| 7 | protected SubclassB( ) | 10 | protected SubclassB( ) |
| 8 | { | 11 | { |
| 12 | // none/a.<init>(Ljava/lang/String;)V | ||
| 9 | super( "B" ); | 13 | super( "B" ); |
| 10 | 14 | ||
| 15 | // access to a | ||
| 11 | m_numThings = 4; | 16 | m_numThings = 4; |
| 12 | } | 17 | } |
| 13 | 18 | ||
| 14 | @Override | 19 | @Override |
| 20 | // a()V | ||
| 15 | public void doBaseThings( ) | 21 | public void doBaseThings( ) |
| 16 | { | 22 | { |
| 17 | System.out.println( "Base things by B!" ); | 23 | // call to none/a.a()Ljava/lang/String; |
| 24 | System.out.println( "Base things by B! " + getName() ); | ||
| 18 | } | 25 | } |
| 19 | 26 | ||
| 27 | // b()V | ||
| 20 | public void doBThings( ) | 28 | public void doBThings( ) |
| 21 | { | 29 | { |
| 30 | // access to a | ||
| 22 | System.out.println( "" + m_numThings + " B things!" ); | 31 | System.out.println( "" + m_numThings + " B things!" ); |
| 23 | } | 32 | } |
| 24 | } | 33 | } |
diff --git a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java index 11196d16..b3b83429 100644 --- a/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java +++ b/test/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java | |||
| @@ -1,21 +1,27 @@ | |||
| 1 | package cuchaz.enigma.inputs.inheritanceTree; | 1 | package cuchaz.enigma.inputs.inheritanceTree; |
| 2 | 2 | ||
| 3 | // none/d extends none/b | ||
| 3 | public class SubsubclassAA extends SubclassA | 4 | public class SubsubclassAA extends SubclassA |
| 4 | { | 5 | { |
| 5 | protected SubsubclassAA( ) | 6 | protected SubsubclassAA( ) |
| 6 | { | 7 | { |
| 8 | // call to none/b.<init>(Ljava/lang/String;)V | ||
| 7 | super( "AA" ); | 9 | super( "AA" ); |
| 8 | } | 10 | } |
| 9 | 11 | ||
| 10 | @Override | 12 | @Override |
| 13 | // a()Ljava/lang/String; | ||
| 11 | public String getName( ) | 14 | public String getName( ) |
| 12 | { | 15 | { |
| 16 | // call to none/b.a()Ljava/lang/String; | ||
| 13 | return "subsub" + super.getName(); | 17 | return "subsub" + super.getName(); |
| 14 | } | 18 | } |
| 15 | 19 | ||
| 16 | @Override | 20 | @Override |
| 21 | // a()V | ||
| 17 | public void doBaseThings( ) | 22 | public void doBaseThings( ) |
| 18 | { | 23 | { |
| 24 | // call to none/d.a()Ljava/lang/String; | ||
| 19 | System.out.println( "Base things by " + getName() ); | 25 | System.out.println( "Base things by " + getName() ); |
| 20 | } | 26 | } |
| 21 | } | 27 | } |