diff options
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis/SourceIndex.java')
| -rw-r--r-- | src/main/java/cuchaz/enigma/analysis/SourceIndex.java | 305 |
1 files changed, 152 insertions, 153 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java index 719930e..19250c8 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java | |||
| @@ -8,174 +8,173 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.HashMultimap; | 14 | import com.google.common.collect.HashMultimap; |
| 14 | import com.google.common.collect.Lists; | 15 | import com.google.common.collect.Lists; |
| 15 | import com.google.common.collect.Maps; | 16 | import com.google.common.collect.Maps; |
| 16 | import com.google.common.collect.Multimap; | 17 | import com.google.common.collect.Multimap; |
| 17 | |||
| 18 | import com.strobel.decompiler.languages.Region; | 18 | import com.strobel.decompiler.languages.Region; |
| 19 | import com.strobel.decompiler.languages.java.ast.AstNode; | 19 | import com.strobel.decompiler.languages.java.ast.AstNode; |
| 20 | import com.strobel.decompiler.languages.java.ast.Identifier; | 20 | import com.strobel.decompiler.languages.java.ast.Identifier; |
| 21 | import cuchaz.enigma.mapping.Entry; | ||
| 21 | 22 | ||
| 22 | import java.util.Collection; | 23 | import java.util.Collection; |
| 23 | import java.util.List; | 24 | import java.util.List; |
| 24 | import java.util.Map; | 25 | import java.util.Map; |
| 25 | import java.util.TreeMap; | 26 | import java.util.TreeMap; |
| 26 | 27 | ||
| 27 | import cuchaz.enigma.mapping.Entry; | ||
| 28 | |||
| 29 | public class SourceIndex { | 28 | public class SourceIndex { |
| 30 | 29 | ||
| 31 | private String source; | 30 | private String source; |
| 32 | private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference; | 31 | private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference; |
| 33 | private Multimap<EntryReference<Entry, Entry>, Token> referenceToTokens; | 32 | private Multimap<EntryReference<Entry, Entry>, Token> referenceToTokens; |
| 34 | private Map<Entry, Token> declarationToToken; | 33 | private Map<Entry, Token> declarationToToken; |
| 35 | private List<Integer> lineOffsets; | 34 | private List<Integer> lineOffsets; |
| 36 | private boolean ignoreBadTokens; | 35 | private boolean ignoreBadTokens; |
| 37 | 36 | ||
| 38 | public SourceIndex(String source) { | 37 | public SourceIndex(String source) { |
| 39 | this(source, true); | 38 | this(source, true); |
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | public SourceIndex(String source, boolean ignoreBadTokens) { | 41 | public SourceIndex(String source, boolean ignoreBadTokens) { |
| 43 | this.source = source; | 42 | this.source = source; |
| 44 | this.ignoreBadTokens = ignoreBadTokens; | 43 | this.ignoreBadTokens = ignoreBadTokens; |
| 45 | this.tokenToReference = Maps.newTreeMap(); | 44 | this.tokenToReference = Maps.newTreeMap(); |
| 46 | this.referenceToTokens = HashMultimap.create(); | 45 | this.referenceToTokens = HashMultimap.create(); |
| 47 | this.declarationToToken = Maps.newHashMap(); | 46 | this.declarationToToken = Maps.newHashMap(); |
| 48 | this.lineOffsets = Lists.newArrayList(); | 47 | this.lineOffsets = Lists.newArrayList(); |
| 49 | 48 | ||
| 50 | // count the lines | 49 | // count the lines |
| 51 | this.lineOffsets.add(0); | 50 | this.lineOffsets.add(0); |
| 52 | for (int i = 0; i < source.length(); i++) { | 51 | for (int i = 0; i < source.length(); i++) { |
| 53 | if (source.charAt(i) == '\n') { | 52 | if (source.charAt(i) == '\n') { |
| 54 | this.lineOffsets.add(i + 1); | 53 | this.lineOffsets.add(i + 1); |
| 55 | } | 54 | } |
| 56 | } | 55 | } |
| 57 | } | 56 | } |
| 58 | 57 | ||
| 59 | public String getSource() { | 58 | public String getSource() { |
| 60 | return this.source; | 59 | return this.source; |
| 61 | } | 60 | } |
| 62 | 61 | ||
| 63 | public Token getToken(AstNode node) { | 62 | public Token getToken(AstNode node) { |
| 64 | 63 | ||
| 65 | // get the text of the node | 64 | // get the text of the node |
| 66 | String name = ""; | 65 | String name = ""; |
| 67 | if (node instanceof Identifier) { | 66 | if (node instanceof Identifier) { |
| 68 | name = ((Identifier) node).getName(); | 67 | name = ((Identifier) node).getName(); |
| 69 | } | 68 | } |
| 70 | 69 | ||
| 71 | // get a token for this node's region | 70 | // get a token for this node's region |
| 72 | Region region = node.getRegion(); | 71 | Region region = node.getRegion(); |
| 73 | if (region.getBeginLine() == 0 || region.getEndLine() == 0) { | 72 | if (region.getBeginLine() == 0 || region.getEndLine() == 0) { |
| 74 | // DEBUG | 73 | // DEBUG |
| 75 | System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region)); | 74 | System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region)); |
| 76 | return null; | 75 | return null; |
| 77 | } | 76 | } |
| 78 | Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source); | 77 | Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source); |
| 79 | if (token.start == 0) { | 78 | if (token.start == 0) { |
| 80 | // DEBUG | 79 | // DEBUG |
| 81 | System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region)); | 80 | System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region)); |
| 82 | return null; | 81 | return null; |
| 83 | } | 82 | } |
| 84 | 83 | ||
| 85 | // DEBUG | 84 | // DEBUG |
| 86 | // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); | 85 | // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); |
| 87 | 86 | ||
| 88 | // if the token has a $ in it, something's wrong. Ignore this token | 87 | // if the token has a $ in it, something's wrong. Ignore this token |
| 89 | if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) { | 88 | if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) { |
| 90 | // DEBUG | 89 | // DEBUG |
| 91 | System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); | 90 | System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); |
| 92 | return null; | 91 | return null; |
| 93 | } | 92 | } |
| 94 | 93 | ||
| 95 | return token; | 94 | return token; |
| 96 | } | 95 | } |
| 97 | 96 | ||
| 98 | public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { | 97 | public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { |
| 99 | Token token = getToken(node); | 98 | Token token = getToken(node); |
| 100 | if (token != null) { | 99 | if (token != null) { |
| 101 | EntryReference<Entry, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); | 100 | EntryReference<Entry, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); |
| 102 | this.tokenToReference.put(token, deobfReference); | 101 | this.tokenToReference.put(token, deobfReference); |
| 103 | this.referenceToTokens.put(deobfReference, token); | 102 | this.referenceToTokens.put(deobfReference, token); |
| 104 | } | 103 | } |
| 105 | } | 104 | } |
| 106 | 105 | ||
| 107 | public void addDeclaration(AstNode node, Entry deobfEntry) { | 106 | public void addDeclaration(AstNode node, Entry deobfEntry) { |
| 108 | Token token = getToken(node); | 107 | Token token = getToken(node); |
| 109 | if (token != null) { | 108 | if (token != null) { |
| 110 | EntryReference<Entry, Entry> reference = new EntryReference<>(deobfEntry, token.text); | 109 | EntryReference<Entry, Entry> reference = new EntryReference<>(deobfEntry, token.text); |
| 111 | this.tokenToReference.put(token, reference); | 110 | this.tokenToReference.put(token, reference); |
| 112 | this.referenceToTokens.put(reference, token); | 111 | this.referenceToTokens.put(reference, token); |
| 113 | this.declarationToToken.put(deobfEntry, token); | 112 | this.declarationToToken.put(deobfEntry, token); |
| 114 | } | 113 | } |
| 115 | } | 114 | } |
| 116 | 115 | ||
| 117 | public Token getReferenceToken(int pos) { | 116 | public Token getReferenceToken(int pos) { |
| 118 | Token token = this.tokenToReference.floorKey(new Token(pos, pos, null)); | 117 | Token token = this.tokenToReference.floorKey(new Token(pos, pos, null)); |
| 119 | if (token != null && token.contains(pos)) { | 118 | if (token != null && token.contains(pos)) { |
| 120 | return token; | 119 | return token; |
| 121 | } | 120 | } |
| 122 | return null; | 121 | return null; |
| 123 | } | 122 | } |
| 124 | 123 | ||
| 125 | public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) { | 124 | public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) { |
| 126 | return this.referenceToTokens.get(deobfReference); | 125 | return this.referenceToTokens.get(deobfReference); |
| 127 | } | 126 | } |
| 128 | 127 | ||
| 129 | public EntryReference<Entry, Entry> getDeobfReference(Token token) { | 128 | public EntryReference<Entry, Entry> getDeobfReference(Token token) { |
| 130 | if (token == null) { | 129 | if (token == null) { |
| 131 | return null; | 130 | return null; |
| 132 | } | 131 | } |
| 133 | return this.tokenToReference.get(token); | 132 | return this.tokenToReference.get(token); |
| 134 | } | 133 | } |
| 135 | 134 | ||
| 136 | public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) { | 135 | public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) { |
| 137 | EntryReference<Entry, Entry> oldDeobfReference = this.tokenToReference.get(token); | 136 | EntryReference<Entry, Entry> oldDeobfReference = this.tokenToReference.get(token); |
| 138 | this.tokenToReference.put(token, newDeobfReference); | 137 | this.tokenToReference.put(token, newDeobfReference); |
| 139 | Collection<Token> tokens = this.referenceToTokens.get(oldDeobfReference); | 138 | Collection<Token> tokens = this.referenceToTokens.get(oldDeobfReference); |
| 140 | this.referenceToTokens.removeAll(oldDeobfReference); | 139 | this.referenceToTokens.removeAll(oldDeobfReference); |
| 141 | this.referenceToTokens.putAll(newDeobfReference, tokens); | 140 | this.referenceToTokens.putAll(newDeobfReference, tokens); |
| 142 | } | 141 | } |
| 143 | 142 | ||
| 144 | public Iterable<Token> referenceTokens() { | 143 | public Iterable<Token> referenceTokens() { |
| 145 | return this.tokenToReference.keySet(); | 144 | return this.tokenToReference.keySet(); |
| 146 | } | 145 | } |
| 147 | 146 | ||
| 148 | public Iterable<Token> declarationTokens() { | 147 | public Iterable<Token> declarationTokens() { |
| 149 | return this.declarationToToken.values(); | 148 | return this.declarationToToken.values(); |
| 150 | } | 149 | } |
| 151 | 150 | ||
| 152 | public Iterable<Entry> declarations() { | 151 | public Iterable<Entry> declarations() { |
| 153 | return this.declarationToToken.keySet(); | 152 | return this.declarationToToken.keySet(); |
| 154 | } | 153 | } |
| 155 | 154 | ||
| 156 | public Token getDeclarationToken(Entry deobfEntry) { | 155 | public Token getDeclarationToken(Entry deobfEntry) { |
| 157 | return this.declarationToToken.get(deobfEntry); | 156 | return this.declarationToToken.get(deobfEntry); |
| 158 | } | 157 | } |
| 159 | 158 | ||
| 160 | public int getLineNumber(int pos) { | 159 | public int getLineNumber(int pos) { |
| 161 | // line number is 1-based | 160 | // line number is 1-based |
| 162 | int line = 0; | 161 | int line = 0; |
| 163 | for (Integer offset : this.lineOffsets) { | 162 | for (Integer offset : this.lineOffsets) { |
| 164 | if (offset > pos) { | 163 | if (offset > pos) { |
| 165 | break; | 164 | break; |
| 166 | } | 165 | } |
| 167 | line++; | 166 | line++; |
| 168 | } | 167 | } |
| 169 | return line; | 168 | return line; |
| 170 | } | 169 | } |
| 171 | 170 | ||
| 172 | public int getColumnNumber(int pos) { | 171 | public int getColumnNumber(int pos) { |
| 173 | // column number is 1-based | 172 | // column number is 1-based |
| 174 | return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1; | 173 | return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1; |
| 175 | } | 174 | } |
| 176 | 175 | ||
| 177 | private int toPos(int line, int col) { | 176 | private int toPos(int line, int col) { |
| 178 | // line and col are 1-based | 177 | // line and col are 1-based |
| 179 | return this.lineOffsets.get(line - 1) + col - 1; | 178 | return this.lineOffsets.get(line - 1) + col - 1; |
| 180 | } | 179 | } |
| 181 | } | 180 | } |