summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis/JarIndex.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cuchaz/enigma/analysis/JarIndex.java')
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java155
1 files changed, 138 insertions, 17 deletions
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java
index 9962bfa..34e8986 100644
--- a/src/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -10,7 +10,9 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.analysis; 11package cuchaz.enigma.analysis;
12 12
13import java.util.AbstractMap;
13import java.util.Collection; 14import java.util.Collection;
15import java.util.Iterator;
14import java.util.List; 16import java.util.List;
15import java.util.Map; 17import java.util.Map;
16import java.util.Set; 18import java.util.Set;
@@ -37,6 +39,7 @@ import com.google.common.collect.Maps;
37import com.google.common.collect.Multimap; 39import com.google.common.collect.Multimap;
38import com.google.common.collect.Sets; 40import com.google.common.collect.Sets;
39 41
42import cuchaz.enigma.mapping.ArgumentEntry;
40import cuchaz.enigma.mapping.ClassEntry; 43import cuchaz.enigma.mapping.ClassEntry;
41import cuchaz.enigma.mapping.ConstructorEntry; 44import cuchaz.enigma.mapping.ConstructorEntry;
42import cuchaz.enigma.mapping.Entry; 45import cuchaz.enigma.mapping.Entry;
@@ -53,6 +56,7 @@ public class JarIndex
53 private Multimap<FieldEntry,Entry> m_fieldCalls; 56 private Multimap<FieldEntry,Entry> m_fieldCalls;
54 private Multimap<String,String> m_innerClasses; 57 private Multimap<String,String> m_innerClasses;
55 private Map<String,String> m_outerClasses; 58 private Map<String,String> m_outerClasses;
59 private Set<String> m_anonymousClasses;
56 60
57 public JarIndex( ) 61 public JarIndex( )
58 { 62 {
@@ -63,6 +67,7 @@ public class JarIndex
63 m_fieldCalls = HashMultimap.create(); 67 m_fieldCalls = HashMultimap.create();
64 m_innerClasses = HashMultimap.create(); 68 m_innerClasses = HashMultimap.create();
65 m_outerClasses = Maps.newHashMap(); 69 m_outerClasses = Maps.newHashMap();
70 m_anonymousClasses = Sets.newHashSet();
66 } 71 }
67 72
68 public void indexJar( JarFile jar ) 73 public void indexJar( JarFile jar )
@@ -84,7 +89,7 @@ public class JarIndex
84 } 89 }
85 } 90 }
86 91
87 // pass 2: index inner classes 92 // pass 2: index inner classes and anonymous classes
88 for( CtClass c : JarClassIterator.classes( jar ) ) 93 for( CtClass c : JarClassIterator.classes( jar ) )
89 { 94 {
90 String outerClassName = isInnerClass( c ); 95 String outerClassName = isInnerClass( c );
@@ -93,8 +98,21 @@ public class JarIndex
93 String innerClassName = Descriptor.toJvmName( c.getName() ); 98 String innerClassName = Descriptor.toJvmName( c.getName() );
94 m_innerClasses.put( outerClassName, innerClassName ); 99 m_innerClasses.put( outerClassName, innerClassName );
95 m_outerClasses.put( innerClassName, outerClassName ); 100 m_outerClasses.put( innerClassName, outerClassName );
101
102 if( isAnonymousClass( c, outerClassName ) )
103 {
104 m_anonymousClasses.add( innerClassName );
105 }
96 } 106 }
97 } 107 }
108
109 // step 3: update other indicies with inner class info
110 Map<String,String> renames = Maps.newHashMap();
111 for( Map.Entry<String,String> entry : m_outerClasses.entrySet() )
112 {
113 renames.put( entry.getKey(), entry.getValue() + "$" + entry.getKey() );
114 }
115 renameClasses( renames );
98 } 116 }
99 117
100 private void indexBehavior( CtBehavior behavior ) 118 private void indexBehavior( CtBehavior behavior )
@@ -186,14 +204,10 @@ public class JarIndex
186 @SuppressWarnings( "unchecked" ) 204 @SuppressWarnings( "unchecked" )
187 private String isInnerClass( CtClass c ) 205 private String isInnerClass( CtClass c )
188 { 206 {
189 String innerClassName = Descriptor.toJvmName( c.getName() ); 207 // inner classes:
190
191 // first, is this an anonymous class?
192 // for anonymous classes:
193 // the outer class is always a synthetic field 208 // the outer class is always a synthetic field
194 // there's at least one constructor with the type of the synthetic field as an argument 209 // there's at least one constructor with the type of the synthetic field as an argument
195 // this constructor is called exactly once by the class of the synthetic field 210
196
197 for( FieldInfo field : (List<FieldInfo>)c.getClassFile().getFields() ) 211 for( FieldInfo field : (List<FieldInfo>)c.getClassFile().getFields() )
198 { 212 {
199 boolean isSynthetic = (field.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; 213 boolean isSynthetic = (field.getAccessFlags() & AccessFlag.SYNTHETIC) != 0;
@@ -230,16 +244,8 @@ public class JarIndex
230 String argumentClassName = Descriptor.toJvmName( Descriptor.toClassName( argumentDesc ) ); 244 String argumentClassName = Descriptor.toJvmName( Descriptor.toClassName( argumentDesc ) );
231 if( argumentClassName.equals( outerClassName ) ) 245 if( argumentClassName.equals( outerClassName ) )
232 { 246 {
233 // is this constructor called exactly once? 247 targetConstructor = constructor;
234 ConstructorEntry constructorEntry = new ConstructorEntry( 248 break;
235 new ClassEntry( innerClassName ),
236 constructor.getMethodInfo().getDescriptor()
237 );
238 if( this.getMethodCallers( constructorEntry ).size() == 1 )
239 {
240 targetConstructor = constructor;
241 break;
242 }
243 } 249 }
244 } 250 }
245 } 251 }
@@ -255,6 +261,30 @@ public class JarIndex
255 return null; 261 return null;
256 } 262 }
257 263
264 private boolean isAnonymousClass( CtClass c, String outerClassName )
265 {
266 String innerClassName = Descriptor.toJvmName( c.getName() );
267
268 // anonymous classes:
269 // have only one constructor
270 // it's called exactly once by the outer class
271 // type of inner class not referenced anywhere in outer class
272
273 // is there exactly one constructor?
274 if( c.getDeclaredConstructors().length != 1 )
275 {
276 return false;
277 }
278 CtConstructor constructor = c.getDeclaredConstructors()[0];
279
280 // is this constructor called exactly once?
281 ConstructorEntry constructorEntry = new ConstructorEntry(
282 new ClassEntry( innerClassName ),
283 constructor.getMethodInfo().getDescriptor()
284 );
285 return getMethodCallers( constructorEntry ).size() == 1;
286 }
287
258 public Set<String> getObfClassNames( ) 288 public Set<String> getObfClassNames( )
259 { 289 {
260 return m_obfClassNames; 290 return m_obfClassNames;
@@ -343,4 +373,95 @@ public class JarIndex
343 { 373 {
344 return m_outerClasses.get( obfInnerClassName ); 374 return m_outerClasses.get( obfInnerClassName );
345 } 375 }
376
377 public boolean isAnonymousClass( String obfInnerClassName )
378 {
379 return m_anonymousClasses.contains( obfInnerClassName );
380 }
381
382 private void renameClasses( Map<String,String> renames )
383 {
384 m_ancestries.renameClasses( renames );
385 renameMultimap( renames, m_methodImplementations );
386 renameMultimap( renames, m_methodCalls );
387 renameMultimap( renames, m_fieldCalls );
388 }
389
390 private <T,U> void renameMultimap( Map<String,String> renames, Multimap<T,U> map )
391 {
392 // for each key/value pair...
393 Set<Map.Entry<T,U>> entriesToAdd = Sets.newHashSet();
394 Iterator<Map.Entry<T,U>> iter = map.entries().iterator();
395 while( iter.hasNext() )
396 {
397 Map.Entry<T,U> entry = iter.next();
398 iter.remove();
399 entriesToAdd.add( new AbstractMap.SimpleEntry<T,U>(
400 renameEntry( renames, entry.getKey() ),
401 renameEntry( renames, entry.getValue() )
402 ) );
403 }
404 for( Map.Entry<T,U> entry : entriesToAdd )
405 {
406 map.put( entry.getKey(), entry.getValue() );
407 }
408 }
409
410 @SuppressWarnings( "unchecked" )
411 private <T> T renameEntry( Map<String,String> renames, T entry )
412 {
413 if( entry instanceof String )
414 {
415 String stringEntry = (String)entry;
416 if( renames.containsKey( stringEntry ) )
417 {
418 return (T)renames.get( stringEntry );
419 }
420 }
421 else if( entry instanceof ClassEntry )
422 {
423 ClassEntry classEntry = (ClassEntry)entry;
424 return (T)new ClassEntry( renameEntry( renames, classEntry.getClassName() ) );
425 }
426 else if( entry instanceof FieldEntry )
427 {
428 FieldEntry fieldEntry = (FieldEntry)entry;
429 return (T)new FieldEntry(
430 renameEntry( renames, fieldEntry.getClassEntry() ),
431 fieldEntry.getName()
432 );
433 }
434 else if( entry instanceof ConstructorEntry )
435 {
436 ConstructorEntry constructorEntry = (ConstructorEntry)entry;
437 return (T)new ConstructorEntry(
438 renameEntry( renames, constructorEntry.getClassEntry() ),
439 constructorEntry.getSignature()
440 );
441 }
442 else if( entry instanceof MethodEntry )
443 {
444 MethodEntry methodEntry = (MethodEntry)entry;
445 return (T)new MethodEntry(
446 renameEntry( renames, methodEntry.getClassEntry() ),
447 methodEntry.getName(),
448 methodEntry.getSignature()
449 );
450 }
451 else if( entry instanceof ArgumentEntry )
452 {
453 ArgumentEntry argumentEntry = (ArgumentEntry)entry;
454 return (T)new ArgumentEntry(
455 renameEntry( renames, argumentEntry.getMethodEntry() ),
456 argumentEntry.getIndex(),
457 argumentEntry.getName()
458 );
459 }
460 else
461 {
462 throw new Error( "Not an entry: " + entry );
463 }
464
465 return entry;
466 }
346} 467}