diff options
Diffstat (limited to 'src/cuchaz/enigma/convert/ClassMapper.java')
| -rw-r--r-- | src/cuchaz/enigma/convert/ClassMapper.java | 202 |
1 files changed, 128 insertions, 74 deletions
diff --git a/src/cuchaz/enigma/convert/ClassMapper.java b/src/cuchaz/enigma/convert/ClassMapper.java index 5a16a1c..fe48c50 100644 --- a/src/cuchaz/enigma/convert/ClassMapper.java +++ b/src/cuchaz/enigma/convert/ClassMapper.java | |||
| @@ -12,31 +12,20 @@ package cuchaz.enigma.convert; | |||
| 12 | 12 | ||
| 13 | import java.io.File; | 13 | import java.io.File; |
| 14 | import java.io.IOException; | 14 | import java.io.IOException; |
| 15 | import java.util.Collection; | 15 | import java.util.Arrays; |
| 16 | import java.util.List; | 16 | import java.util.List; |
| 17 | import java.util.Map; | 17 | import java.util.Map; |
| 18 | import java.util.Set; | ||
| 18 | import java.util.jar.JarFile; | 19 | import java.util.jar.JarFile; |
| 19 | 20 | ||
| 20 | import javassist.CtClass; | 21 | import javassist.CtClass; |
| 21 | 22 | import cuchaz.enigma.TranslatingTypeLoader; | |
| 22 | import com.beust.jcommander.internal.Lists; | 23 | import cuchaz.enigma.analysis.JarIndex; |
| 23 | import com.beust.jcommander.internal.Maps; | 24 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; |
| 24 | import com.google.common.collect.ArrayListMultimap; | ||
| 25 | import com.google.common.collect.Multimap; | ||
| 26 | |||
| 27 | import cuchaz.enigma.analysis.JarClassIterator; | ||
| 28 | import cuchaz.enigma.mapping.ClassEntry; | 25 | import cuchaz.enigma.mapping.ClassEntry; |
| 29 | 26 | ||
| 30 | public class ClassMapper | 27 | public class ClassMapper |
| 31 | { | 28 | { |
| 32 | private int m_numSourceClasses; | ||
| 33 | private int m_numDestClasses; | ||
| 34 | private Multimap<ClassIdentity,ClassIdentity> m_sourceClasses; | ||
| 35 | private Multimap<ClassIdentity,ClassIdentity> m_destClasses; | ||
| 36 | private List<ClassIdentity> m_unmatchedSourceClasses; | ||
| 37 | private List<ClassIdentity> m_unmatchedDestClasses; | ||
| 38 | private Map<ClassEntry,ClassIdentity> m_sourceKeyIndex; | ||
| 39 | |||
| 40 | public static void main( String[] args ) | 29 | public static void main( String[] args ) |
| 41 | throws IOException | 30 | throws IOException |
| 42 | { | 31 | { |
| @@ -44,81 +33,146 @@ public class ClassMapper | |||
| 44 | JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); | 33 | JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); |
| 45 | JarFile toJar = new JarFile( new File( "input/1.8-pre2.jar" ) ); | 34 | JarFile toJar = new JarFile( new File( "input/1.8-pre2.jar" ) ); |
| 46 | 35 | ||
| 47 | ClassMapper mapper = new ClassMapper( fromJar, toJar ); | 36 | // compute the matching |
| 48 | System.out.println( String.format( "Mapped %d/%d source classes (%d unmatched) to %d/%d dest classes (%d unmatched)", | 37 | ClassMatching matching = ClassMapper.computeMatching( fromJar, toJar ); |
| 49 | mapper.m_sourceClasses.size(), mapper.m_numSourceClasses, mapper.m_unmatchedSourceClasses.size(), | 38 | |
| 50 | mapper.m_destClasses.size(), mapper.m_numDestClasses, mapper.m_unmatchedDestClasses.size() | 39 | // TODO: use the matching to convert the mappings |
| 51 | ) ); | ||
| 52 | } | 40 | } |
| 53 | 41 | ||
| 54 | public ClassMapper( JarFile sourceJar, JarFile destJar ) | 42 | public static ClassMatching computeMatching( JarFile sourceJar, JarFile destJar ) |
| 55 | { | 43 | { |
| 56 | m_numSourceClasses = JarClassIterator.getClassEntries( sourceJar ).size(); | 44 | // index jars |
| 57 | m_numDestClasses = JarClassIterator.getClassEntries( destJar ).size(); | 45 | System.out.println( "Indexing source jar..." ); |
| 46 | JarIndex sourceIndex = new JarIndex(); | ||
| 47 | sourceIndex.indexJar( sourceJar ); | ||
| 48 | System.out.println( "Indexing dest jar..." ); | ||
| 49 | JarIndex destIndex = new JarIndex(); | ||
| 50 | destIndex.indexJar( destJar ); | ||
| 58 | 51 | ||
| 59 | // compute identities for the source classes | 52 | System.out.println( "Computing matching..." ); |
| 60 | m_sourceClasses = ArrayListMultimap.create(); | ||
| 61 | m_sourceKeyIndex = Maps.newHashMap(); | ||
| 62 | for( CtClass c : JarClassIterator.classes( sourceJar ) ) | ||
| 63 | { | ||
| 64 | ClassIdentity sourceClass = new ClassIdentity( c ); | ||
| 65 | m_sourceClasses.put( sourceClass, sourceClass ); | ||
| 66 | m_sourceKeyIndex.put( sourceClass.getClassEntry(), sourceClass ); | ||
| 67 | } | ||
| 68 | 53 | ||
| 69 | // match the dest classes to the source classes | 54 | TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader( sourceJar, sourceIndex ); |
| 70 | m_destClasses = ArrayListMultimap.create(); | 55 | TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex ); |
| 71 | m_unmatchedDestClasses = Lists.newArrayList(); | 56 | |
| 72 | for( CtClass c : JarClassIterator.classes( destJar ) ) | 57 | ClassMatching matching = null; |
| 58 | for( boolean useReferences : Arrays.asList( false, true ) ) | ||
| 73 | { | 59 | { |
| 74 | ClassIdentity destClass = new ClassIdentity( c ); | 60 | int numMatches = 0; |
| 75 | Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get( destClass ); | 61 | do |
| 76 | if( matchedSourceClasses.isEmpty() ) | ||
| 77 | { | ||
| 78 | // unmatched dest class | ||
| 79 | m_unmatchedDestClasses.add( destClass ); | ||
| 80 | } | ||
| 81 | else | ||
| 82 | { | 62 | { |
| 83 | ClassIdentity sourceClass = matchedSourceClasses.iterator().next(); | 63 | SidedClassNamer sourceNamer = null; |
| 84 | m_destClasses.put( sourceClass, destClass ); | 64 | SidedClassNamer destNamer = null; |
| 65 | if( matching != null ) | ||
| 66 | { | ||
| 67 | // build a class namer | ||
| 68 | ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); | ||
| 69 | sourceNamer = namer.getSourceNamer(); | ||
| 70 | destNamer = namer.getDestNamer(); | ||
| 71 | |||
| 72 | // note the number of matches | ||
| 73 | numMatches = matching.getUniqueMatches().size(); | ||
| 74 | } | ||
| 75 | |||
| 76 | // get the entries left to match | ||
| 77 | Set<ClassEntry> sourceClassEntries = sourceIndex.getObfClassEntries(); | ||
| 78 | Set<ClassEntry> destClassEntries = destIndex.getObfClassEntries(); | ||
| 79 | if( matching != null ) | ||
| 80 | { | ||
| 81 | sourceClassEntries.clear(); | ||
| 82 | destClassEntries.clear(); | ||
| 83 | for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : matching.getAmbiguousMatches().entrySet() ) | ||
| 84 | { | ||
| 85 | for( ClassIdentity c : entry.getKey() ) | ||
| 86 | { | ||
| 87 | sourceClassEntries.add( c.getClassEntry() ); | ||
| 88 | matching.removeSource( c ); | ||
| 89 | } | ||
| 90 | for( ClassIdentity c : entry.getValue() ) | ||
| 91 | { | ||
| 92 | destClassEntries.add( c.getClassEntry() ); | ||
| 93 | matching.removeDest( c ); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) | ||
| 97 | { | ||
| 98 | sourceClassEntries.add( c.getClassEntry() ); | ||
| 99 | matching.removeSource( c ); | ||
| 100 | } | ||
| 101 | for( ClassIdentity c : matching.getUnmatchedDestClasses() ) | ||
| 102 | { | ||
| 103 | destClassEntries.add( c.getClassEntry() ); | ||
| 104 | matching.removeDest( c ); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | else | ||
| 108 | { | ||
| 109 | matching = new ClassMatching(); | ||
| 110 | } | ||
| 111 | |||
| 112 | // compute a matching for the classes | ||
| 113 | for( ClassEntry classEntry : sourceClassEntries ) | ||
| 114 | { | ||
| 115 | CtClass c = sourceLoader.loadClass( classEntry.getName() ); | ||
| 116 | ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences ); | ||
| 117 | matching.addSource( sourceClass ); | ||
| 118 | } | ||
| 119 | for( ClassEntry classEntry : destClassEntries ) | ||
| 120 | { | ||
| 121 | CtClass c = destLoader.loadClass( classEntry.getName() ); | ||
| 122 | ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences ); | ||
| 123 | matching.matchDestClass( destClass ); | ||
| 124 | } | ||
| 125 | |||
| 126 | // TEMP | ||
| 127 | System.out.println( matching ); | ||
| 85 | } | 128 | } |
| 129 | while( matching.getUniqueMatches().size() - numMatches > 0 ); | ||
| 86 | } | 130 | } |
| 87 | 131 | ||
| 88 | // get unmatched source classes | 132 | /* DEBUG: show some ambiguous matches |
| 89 | m_unmatchedSourceClasses = Lists.newArrayList(); | 133 | List<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>> ambiguousMatches = new ArrayList<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>( matching.getAmbiguousMatches().entrySet() ); |
| 90 | for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) | 134 | Collections.sort( ambiguousMatches, new Comparator<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>( ) |
| 91 | { | 135 | { |
| 92 | Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get( sourceClass ); | 136 | @Override |
| 93 | Collection<ClassIdentity> matchedDestClasses = m_destClasses.get( sourceClass ); | 137 | public int compare( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> a, Map.Entry<List<ClassIdentity>,List<ClassIdentity>> b ) |
| 94 | if( matchedDestClasses.isEmpty() ) | ||
| 95 | { | 138 | { |
| 96 | m_unmatchedSourceClasses.add( sourceClass ); | 139 | String aName = a.getKey().get( 0 ).getClassEntry().getName(); |
| 140 | String bName = b.getKey().get( 0 ).getClassEntry().getName(); | ||
| 141 | return aName.compareTo( bName ); | ||
| 97 | } | 142 | } |
| 98 | else if( matchedDestClasses.size() > 1 ) | 143 | } ); |
| 144 | for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : ambiguousMatches ) | ||
| 145 | { | ||
| 146 | for( ClassIdentity c : entry.getKey() ) | ||
| 99 | { | 147 | { |
| 100 | // warn about identity collisions | 148 | System.out.print( c.getClassEntry().getName() + " " ); |
| 101 | System.err.println( String.format( "WARNING: identity collision:\n\tSource: %s\n\t Dest: %s", | ||
| 102 | getClassEntries( matchedSourceClasses ), | ||
| 103 | getClassEntries( matchedDestClasses ) | ||
| 104 | ) ); | ||
| 105 | } | 149 | } |
| 150 | System.out.println(); | ||
| 151 | } | ||
| 152 | Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry = ambiguousMatches.get( 7 ); | ||
| 153 | for( ClassIdentity c : entry.getKey() ) | ||
| 154 | { | ||
| 155 | System.out.println( c ); | ||
| 106 | } | 156 | } |
| 157 | for( ClassIdentity c : entry.getKey() ) | ||
| 158 | { | ||
| 159 | System.out.println( decompile( sourceLoader, c.getClassEntry() ) ); | ||
| 160 | } | ||
| 161 | */ | ||
| 162 | |||
| 163 | return matching; | ||
| 107 | } | 164 | } |
| 108 | 165 | ||
| 109 | public Map.Entry<Collection<ClassEntry>,Collection<ClassEntry>> getMapping( ClassEntry sourceEntry ) | 166 | /* DEBUG |
| 167 | private static String decompile( TranslatingTypeLoader loader, ClassEntry classEntry ) | ||
| 110 | { | 168 | { |
| 111 | // TODO | 169 | PlainTextOutput output = new PlainTextOutput(); |
| 112 | return null; | 170 | DecompilerSettings settings = DecompilerSettings.javaDefaults(); |
| 113 | } | 171 | settings.setForceExplicitImports( true ); |
| 114 | 172 | settings.setShowSyntheticMembers( true ); | |
| 115 | private Collection<ClassEntry> getClassEntries( Collection<ClassIdentity> classes ) | 173 | settings.setTypeLoader( loader ); |
| 116 | { | 174 | Decompiler.decompile( classEntry.getName(), output, settings ); |
| 117 | List<ClassEntry> entries = Lists.newArrayList(); | 175 | return output.toString(); |
| 118 | for( ClassIdentity c : classes ) | ||
| 119 | { | ||
| 120 | entries.add( c.getClassEntry() ); | ||
| 121 | } | ||
| 122 | return entries; | ||
| 123 | } | 176 | } |
| 177 | */ | ||
| 124 | } | 178 | } |