summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/convert/ClassMapper.java
diff options
context:
space:
mode:
authorGravatar jeff2014-08-30 16:31:31 -0400
committerGravatar jeff2014-08-30 16:31:31 -0400
commit59c592673635e989fd0785d41d51d7c3dd17cc0b (patch)
treeccbf73bd2b45bdfa6901db03100e5ff7f4116220 /src/cuchaz/enigma/convert/ClassMapper.java
parentfinished class matching for now, need to work on class member matching (diff)
downloadenigma-fork-59c592673635e989fd0785d41d51d7c3dd17cc0b.tar.gz
enigma-fork-59c592673635e989fd0785d41d51d7c3dd17cc0b.tar.xz
enigma-fork-59c592673635e989fd0785d41d51d7c3dd17cc0b.zip
debugging class matcher... almost got it!
Diffstat (limited to '')
-rw-r--r--src/cuchaz/enigma/convert/ClassMatcher.java (renamed from src/cuchaz/enigma/convert/ClassMapper.java)265
1 files changed, 183 insertions, 82 deletions
diff --git a/src/cuchaz/enigma/convert/ClassMapper.java b/src/cuchaz/enigma/convert/ClassMatcher.java
index fd6ab92..ac07a5b 100644
--- a/src/cuchaz/enigma/convert/ClassMapper.java
+++ b/src/cuchaz/enigma/convert/ClassMatcher.java
@@ -14,8 +14,12 @@ import java.io.File;
14import java.io.FileReader; 14import java.io.FileReader;
15import java.io.FileWriter; 15import java.io.FileWriter;
16import java.io.IOException; 16import java.io.IOException;
17import java.util.AbstractMap;
18import java.util.ArrayList;
17import java.util.Arrays; 19import java.util.Arrays;
20import java.util.Collection;
18import java.util.Collections; 21import java.util.Collections;
22import java.util.Comparator;
19import java.util.Iterator; 23import java.util.Iterator;
20import java.util.List; 24import java.util.List;
21import java.util.Map; 25import java.util.Map;
@@ -23,12 +27,15 @@ import java.util.Set;
23import java.util.TreeMap; 27import java.util.TreeMap;
24import java.util.jar.JarFile; 28import java.util.jar.JarFile;
25 29
30import javassist.CtBehavior;
26import javassist.CtClass; 31import javassist.CtClass;
27 32
33import com.beust.jcommander.internal.Lists;
28import com.beust.jcommander.internal.Sets; 34import com.beust.jcommander.internal.Sets;
35import com.google.common.collect.BiMap;
36import com.google.common.collect.HashBiMap;
29import com.google.common.collect.Maps; 37import com.google.common.collect.Maps;
30 38
31import cuchaz.enigma.Constants;
32import cuchaz.enigma.TranslatingTypeLoader; 39import cuchaz.enigma.TranslatingTypeLoader;
33import cuchaz.enigma.analysis.JarIndex; 40import cuchaz.enigma.analysis.JarIndex;
34import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; 41import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
@@ -38,73 +45,107 @@ import cuchaz.enigma.mapping.MappingParseException;
38import cuchaz.enigma.mapping.Mappings; 45import cuchaz.enigma.mapping.Mappings;
39import cuchaz.enigma.mapping.MappingsReader; 46import cuchaz.enigma.mapping.MappingsReader;
40import cuchaz.enigma.mapping.MappingsWriter; 47import cuchaz.enigma.mapping.MappingsWriter;
48import cuchaz.enigma.mapping.MethodEntry;
49import cuchaz.enigma.mapping.MethodMapping;
41 50
42public class ClassMapper 51public class ClassMatcher
43{ 52{
44 public static void main( String[] args ) 53 public static void main( String[] args )
45 throws IOException, MappingParseException 54 throws IOException, MappingParseException
46 { 55 {
47 // TEMP 56 // TEMP
48 JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); 57 JarFile sourceJar = new JarFile( new File( "input/1.8-pre1.jar" ) );
49 JarFile toJar = new JarFile( new File( "input/1.8-pre3.jar" ) ); 58 JarFile destJar = new JarFile( new File( "input/1.8-pre2.jar" ) );
50 File inMappingsFile = new File( "../minecraft-mappings/1.8-pre.mappings" ); 59 File inMappingsFile = new File( "../minecraft-mappings/1.8-pre.mappings" );
51 File outMappingsFile = new File( "../minecraft-mappings/1.8-pre3.mappings" ); 60 File outMappingsFile = new File( "../minecraft-mappings/1.8-pre2.mappings" );
52 61
62 // do the conversion
63 Mappings mappings = new MappingsReader().read( new FileReader( inMappingsFile ) );
64 convertMappings( sourceJar, destJar, mappings );
65
66 // write out the convert mappings
67 FileWriter writer = new FileWriter( outMappingsFile );
68 new MappingsWriter().write( writer, mappings );
69 writer.close();
70 System.out.println( "Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath() );
71 }
72
73 private static void convertMappings( JarFile sourceJar, JarFile destJar, Mappings mappings )
74 {
75 // index jars
76 System.out.println( "Indexing source jar..." );
77 JarIndex sourceIndex = new JarIndex();
78 sourceIndex.indexJar( sourceJar, false );
79 System.out.println( "Indexing dest jar..." );
80 JarIndex destIndex = new JarIndex();
81 destIndex.indexJar( destJar, false );
82 TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader( sourceJar, sourceIndex );
83 TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex );
84
53 // compute the matching 85 // compute the matching
54 ClassMatching matching = ClassMapper.computeMatching( fromJar, toJar ); 86 ClassMatching matching = ClassMatcher.computeMatching( sourceIndex, sourceLoader, destIndex, destLoader );
55 87
56 // use the matching to convert the mappings 88 // start the class conversion map with the unique and ambiguous matchings
57 Mappings mappings = new MappingsReader().read( new FileReader( inMappingsFile ) );
58 Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> conversionMap = matching.getConversionMap(); 89 Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> conversionMap = matching.getConversionMap();
59 Map<String,String> finalConversion = Maps.newHashMap(); 90
91 // probabilistically match the unmatched source classes
92 for( ClassIdentity sourceClass : new ArrayList<ClassIdentity>( matching.getUnmatchedSourceClasses() ) )
93 {
94 System.out.println( "No exact match for source class " + sourceClass.getClassEntry() );
95
96 // find the closest classes
97 TreeMap<Integer,ClassIdentity> scoredMatches = Maps.newTreeMap( Collections.reverseOrder() );
98 for( ClassIdentity c : matching.getUnmatchedDestClasses() )
99 {
100 scoredMatches.put( sourceClass.getMatchScore( c ), c );
101 }
102 Iterator<Map.Entry<Integer,ClassIdentity>> iter = scoredMatches.entrySet().iterator();
103 for( int i=0; i<10 && iter.hasNext(); i++ )
104 {
105 Map.Entry<Integer,ClassIdentity> score = iter.next();
106 System.out.println( String.format( "\tScore: %3d %s", score.getKey(), score.getValue().getClassEntry().getName() ) );
107 }
108
109 // does the best match have a non-zero score and the same name?
110 Map.Entry<Integer,ClassIdentity> bestMatch = scoredMatches.firstEntry();
111 if( bestMatch.getKey() > 0 && bestMatch.getValue().getClassEntry().equals( sourceClass.getClassEntry() ) )
112 {
113 // use it
114 System.out.println( "\tAutomatically choosing likely match: " + bestMatch.getValue().getClassEntry().getName() );
115 conversionMap.put(
116 sourceClass.getClassEntry().getName(),
117 new AbstractMap.SimpleEntry<ClassIdentity,List<ClassIdentity>>( sourceClass, Arrays.asList( bestMatch.getValue() ) )
118 );
119 }
120 }
121
122 // use the matching to convert the mappings
123 BiMap<String,String> classConversion = HashBiMap.create();
60 Set<String> unmatchedSourceClasses = Sets.newHashSet(); 124 Set<String> unmatchedSourceClasses = Sets.newHashSet();
61 for( ClassMapping classMapping : mappings.classes() ) 125 for( String className : mappings.getAllObfClassNames() )
62 { 126 {
63 // is there a match for this class? 127 // is there a match for this class?
64 Map.Entry<ClassIdentity,List<ClassIdentity>> entry = conversionMap.get( classMapping.getObfName() ); 128 Map.Entry<ClassIdentity,List<ClassIdentity>> entry = conversionMap.get( className );
65 ClassIdentity sourceClass = entry.getKey(); 129 ClassIdentity sourceClass = entry.getKey();
66 List<ClassIdentity> matches = entry.getValue(); 130 List<ClassIdentity> matches = entry.getValue();
67 131
68 if( matches.isEmpty() ) 132 if( matches.isEmpty() )
69 { 133 {
70 // no match! =( 134 // no match! =(
71 System.out.println( "No exact match for source class " + classMapping.getObfName() ); 135 unmatchedSourceClasses.add( className );
72
73 // find the closest classes
74 TreeMap<Integer,ClassIdentity> scoredMatches = Maps.newTreeMap( Collections.reverseOrder() );
75 for( ClassIdentity c : matching.getUnmatchedDestClasses() )
76 {
77 scoredMatches.put( sourceClass.getMatchScore( c ), c );
78 }
79 Iterator<Map.Entry<Integer,ClassIdentity>> iter = scoredMatches.entrySet().iterator();
80 for( int i=0; i<10 && iter.hasNext(); i++ )
81 {
82 Map.Entry<Integer,ClassIdentity> score = iter.next();
83 System.out.println( String.format( "\tScore: %3d %s", score.getKey(), score.getValue().getClassEntry().getName() ) );
84 }
85
86 // does the best match have a non-zero score and the same name?
87 Map.Entry<Integer,ClassIdentity> bestMatch = scoredMatches.firstEntry();
88 if( bestMatch.getKey() > 0 && bestMatch.getValue().getClassEntry().equals( sourceClass.getClassEntry() ) )
89 {
90 // use it
91 System.out.println( "\tAutomatically choosing likely match: " + bestMatch.getValue().getClassEntry().getName() );
92 addFinalConversion( finalConversion, sourceClass, bestMatch.getValue() );
93 }
94 else
95 {
96 unmatchedSourceClasses.add( classMapping.getObfName() );
97 }
98 } 136 }
99 if( matches.size() == 1 ) 137 else if( matches.size() == 1 )
100 { 138 {
101 // unique match! We're good to go! 139 // unique match! We're good to go!
102 addFinalConversion( finalConversion, sourceClass, matches.get( 0 ) ); 140 classConversion.put(
141 sourceClass.getClassEntry().getName(),
142 matches.get( 0 ).getClassEntry().getName()
143 );
103 } 144 }
104 else if( matches.size() > 1 ) 145 else if( matches.size() > 1 )
105 { 146 {
106 // too many matches! =( 147 // too many matches! =(
107 unmatchedSourceClasses.add( classMapping.getObfName() ); 148 unmatchedSourceClasses.add( className );
108 } 149 }
109 } 150 }
110 151
@@ -121,39 +162,85 @@ public class ClassMapper
121 } 162 }
122 163
123 // show the class name changes 164 // show the class name changes
124 for( Map.Entry<String,String> entry : finalConversion.entrySet() ) 165 for( Map.Entry<String,String> entry : classConversion.entrySet() )
125 { 166 {
126 if( !entry.getKey().equals( entry.getValue() ) ) 167 if( !entry.getKey().equals( entry.getValue() ) )
127 { 168 {
128 System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) ); 169 System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) );
170 /* DEBUG
171 System.out.println( String.format( "\n%s\n%s",
172 new ClassIdentity( sourceLoader.loadClass( entry.getKey() ), null, sourceIndex, false, false ),
173 new ClassIdentity( destLoader.loadClass( entry.getValue() ), null, destIndex, false, false )
174 ) );
175 */
129 } 176 }
130 } 177 }
131 178
132 // do the final conversion 179 // TEMP: show some classes
133 mappings.renameObfClasses( finalConversion ); 180 for( String className : Arrays.asList( "none/em", "none/ej", "none/en" ) )
134 FileWriter writer = new FileWriter( outMappingsFile ); 181 {
135 new MappingsWriter().write( writer, mappings ); 182 System.out.println( String.format( "check: %s -> %s", className, classConversion.get( className ) ) );
136 writer.close(); 183 }
137 System.out.println( "Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath() ); 184
185 // convert the classes
186 mappings.renameObfClasses( classConversion );
187
188 // look for method matches
189 System.out.println( "Matching methods..." );
190 for( ClassMapping classMapping : mappings.classes() )
191 {
192 ClassEntry classEntry = new ClassEntry( classMapping.getObfName() );
193 for( MethodMapping methodMapping : classMapping.methods() )
194 {
195 // skip constructors
196 if( methodMapping.getObfName().equals( "<init>" ) )
197 {
198 continue;
199 }
200
201 MethodEntry methodEntry = new MethodEntry(
202 classEntry,
203 methodMapping.getObfName(),
204 methodMapping.getObfSignature()
205 );
206 if( !destIndex.isMethodImplemented( methodEntry ) )
207 {
208 System.err.println( "WARNING: method doesn't match: " + methodEntry );
209
210 // show the available methods
211 System.err.println( "\tAvailable dest methods:" );
212 CtClass c = destLoader.loadClass( classMapping.getObfName() );
213 for( CtBehavior behavior : c.getDeclaredBehaviors() )
214 {
215 MethodEntry declaredMethodEntry = new MethodEntry(
216 new ClassEntry( classMapping.getObfName() ),
217 behavior.getName(),
218 behavior.getSignature()
219 );
220 System.err.println( "\t\t" + declaredMethodEntry );
221 }
222
223 System.err.println( "\tAvailable source methods:" );
224 c = sourceLoader.loadClass( classConversion.inverse().get( classMapping.getObfName() ) );
225 for( CtBehavior behavior : c.getDeclaredBehaviors() )
226 {
227 MethodEntry declaredMethodEntry = new MethodEntry(
228 new ClassEntry( classMapping.getObfName() ),
229 behavior.getName(),
230 behavior.getSignature()
231 );
232 System.err.println( "\t\t" + declaredMethodEntry );
233 }
234 }
235 }
236 }
138 } 237 }
139 238
140 public static ClassMatching computeMatching( JarFile sourceJar, JarFile destJar ) 239 public static ClassMatching computeMatching( JarIndex sourceIndex, TranslatingTypeLoader sourceLoader, JarIndex destIndex, TranslatingTypeLoader destLoader )
141 { 240 {
142 // index jars 241 System.out.println( "Matching classes..." );
143 System.out.println( "Indexing source jar..." );
144 JarIndex sourceIndex = new JarIndex();
145 sourceIndex.indexJar( sourceJar );
146 System.out.println( "Indexing dest jar..." );
147 JarIndex destIndex = new JarIndex();
148 destIndex.indexJar( destJar );
149
150 System.out.println( "Computing matching..." );
151
152 TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader( sourceJar, sourceIndex );
153 TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex );
154
155 ClassMatching matching = null; 242 ClassMatching matching = null;
156 for( boolean useRawNames : Arrays.asList( false, true ) ) 243 for( boolean useRawNames : Arrays.asList( false/*, true*/ ) )
157 { 244 {
158 for( boolean useReferences : Arrays.asList( false, true ) ) 245 for( boolean useReferences : Arrays.asList( false, true ) )
159 { 246 {
@@ -230,7 +317,28 @@ public class ClassMapper
230 } 317 }
231 } 318 }
232 319
233 /* DEBUG: show some ambiguous matches 320 // DEBUG: check the class matches
321 System.out.println( "Checking class matches..." );
322 for( Map.Entry<ClassIdentity,ClassIdentity> entry : matching.getUniqueMatches().entrySet() )
323 {
324 // check source
325 ClassIdentity sourceClass = entry.getKey();
326 CtClass sourceC = sourceLoader.loadClass( sourceClass.getClassEntry().getName() );
327 assert( sourceC != null )
328 : "Unable to load source class " + sourceClass.getClassEntry();
329 assert( sourceClass.matches( sourceC ) )
330 : "Source " + sourceClass + " doesn't match " + new ClassIdentity( sourceC, null, sourceIndex, false, false );
331
332 // check dest
333 ClassIdentity destClass = entry.getValue();
334 CtClass destC = destLoader.loadClass( destClass.getClassEntry().getName() );
335 assert( destC != null )
336 : "Unable to load dest class " + destClass.getClassEntry();
337 assert( destClass.matches( destC ) )
338 : "Dest " + destClass + " doesn't match " + new ClassIdentity( destC, null, destIndex, false, false );
339 }
340
341 // warn about the ambiguous matchings
234 List<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>> ambiguousMatches = new ArrayList<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>( matching.getAmbiguousMatches().entrySet() ); 342 List<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>> ambiguousMatches = new ArrayList<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>( matching.getAmbiguousMatches().entrySet() );
235 Collections.sort( ambiguousMatches, new Comparator<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>( ) 343 Collections.sort( ambiguousMatches, new Comparator<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>( )
236 { 344 {
@@ -244,12 +352,12 @@ public class ClassMapper
244 } ); 352 } );
245 for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : ambiguousMatches ) 353 for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : ambiguousMatches )
246 { 354 {
247 for( ClassIdentity c : entry.getKey() ) 355 System.out.println( "Ambiguous matching:" );
248 { 356 System.out.println( "\tSource: " + getClassNames( entry.getKey() ) );
249 System.out.print( c.getClassEntry().getName() + " " ); 357 System.out.println( "\tDest: " + getClassNames( entry.getValue() ) );
250 }
251 System.out.println();
252 } 358 }
359
360 /* DEBUG
253 Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry = ambiguousMatches.get( 7 ); 361 Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry = ambiguousMatches.get( 7 );
254 for( ClassIdentity c : entry.getKey() ) 362 for( ClassIdentity c : entry.getKey() )
255 { 363 {
@@ -264,22 +372,15 @@ public class ClassMapper
264 return matching; 372 return matching;
265 } 373 }
266 374
267 private static void addFinalConversion( Map<String,String> finalConversion, ClassIdentity sourceClass, ClassIdentity destClass ) 375 private static List<String> getClassNames( Collection<ClassIdentity> classes )
268 { 376 {
269 // flatten inner classes since these are all obf classes in the none package 377 List<String> out = Lists.newArrayList();
270 String sourceClassName = sourceClass.getClassEntry().getName(); 378 for( ClassIdentity c : classes )
271 if( sourceClass.getClassEntry().isInnerClass() )
272 { 379 {
273 sourceClassName = Constants.NonePackage + "/" + sourceClass.getClassEntry().getInnerClassName(); 380 out.add( c.getClassEntry().getName() );
274 } 381 }
275 382 Collections.sort( out );
276 String destClassName = destClass.getClassEntry().getName(); 383 return out;
277 if( destClass.getClassEntry().isInnerClass() )
278 {
279 destClassName = Constants.NonePackage + "/" + destClass.getClassEntry().getInnerClassName();
280 }
281
282 finalConversion.put( sourceClassName, destClassName );
283 } 384 }
284 385
285 /* DEBUG 386 /* DEBUG