summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
diff options
context:
space:
mode:
authorGravatar Runemoro2020-03-09 06:04:08 -0400
committerGravatar GitHub2020-03-09 10:04:08 +0000
commit58c0aeb15a65324de08a914dfa62cc68a516a4e3 (patch)
treef45e8141c0864692051149a478c5a0a6bbe68686 /src/main/java/cuchaz/enigma/analysis/SourceIndex.java
parentMade Enigma gui translatable (#193) (diff)
downloadenigma-fork-58c0aeb15a65324de08a914dfa62cc68a516a4e3.tar.gz
enigma-fork-58c0aeb15a65324de08a914dfa62cc68a516a4e3.tar.xz
enigma-fork-58c0aeb15a65324de08a914dfa62cc68a516a4e3.zip
CFR support (#192)
* Add decompiler API * Add CFR support
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis/SourceIndex.java')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java240
1 files changed, 0 insertions, 240 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
deleted file mode 100644
index a800f43..0000000
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
+++ /dev/null
@@ -1,240 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Lists;
16import com.google.common.collect.Maps;
17import com.google.common.collect.Multimap;
18import com.strobel.decompiler.languages.Region;
19import com.strobel.decompiler.languages.java.ast.AstNode;
20import com.strobel.decompiler.languages.java.ast.CompilationUnit;
21import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration;
22import com.strobel.decompiler.languages.java.ast.Identifier;
23import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
24import cuchaz.enigma.gui.SourceRemapper;
25import cuchaz.enigma.translation.mapping.EntryResolver;
26import cuchaz.enigma.translation.mapping.ResolutionStrategy;
27import cuchaz.enigma.translation.representation.entry.Entry;
28
29import javax.annotation.Nullable;
30import java.util.Collection;
31import java.util.List;
32import java.util.Map;
33import java.util.TreeMap;
34import java.util.regex.Pattern;
35import java.util.stream.Collectors;
36
37public class SourceIndex {
38 private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$");
39
40 private String source;
41 private TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReference;
42 private Multimap<EntryReference<Entry<?>, Entry<?>>, Token> referenceToTokens;
43 private Map<Entry<?>, Token> declarationToToken;
44 private List<Integer> lineOffsets;
45 private boolean ignoreBadTokens;
46
47 public SourceIndex(String source) {
48 this(source, true);
49 }
50
51 public SourceIndex(String source, boolean ignoreBadTokens) {
52 this.source = source;
53 this.ignoreBadTokens = ignoreBadTokens;
54 this.tokenToReference = new TreeMap<>();
55 this.referenceToTokens = HashMultimap.create();
56 this.declarationToToken = Maps.newHashMap();
57 calculateLineOffsets();
58 }
59
60 public static SourceIndex buildIndex(String sourceString, CompilationUnit sourceTree, boolean ignoreBadTokens) {
61 SourceIndex index = new SourceIndex(sourceString, ignoreBadTokens);
62 sourceTree.acceptVisitor(new SourceIndexVisitor(), index);
63
64 return index;
65 }
66
67 private void calculateLineOffsets() {
68 // count the lines
69 this.lineOffsets = Lists.newArrayList();
70 this.lineOffsets.add(0);
71 for (int i = 0; i < source.length(); i++) {
72 if (source.charAt(i) == '\n') {
73 this.lineOffsets.add(i + 1);
74 }
75 }
76 }
77
78 public SourceIndex remapTo(SourceRemapper.Result result) {
79 SourceIndex remapped = new SourceIndex(result.getSource(), ignoreBadTokens);
80
81 for (Map.Entry<Entry<?>, Token> entry : declarationToToken.entrySet()) {
82 remapped.declarationToToken.put(entry.getKey(), result.getRemappedToken(entry.getValue()));
83 }
84
85 for (Map.Entry<EntryReference<Entry<?>, Entry<?>>, Collection<Token>> entry : referenceToTokens.asMap().entrySet()) {
86 EntryReference<Entry<?>, Entry<?>> reference = entry.getKey();
87 Collection<Token> oldTokens = entry.getValue();
88
89 Collection<Token> newTokens = oldTokens.stream()
90 .map(result::getRemappedToken)
91 .collect(Collectors.toList());
92
93 remapped.referenceToTokens.putAll(reference, newTokens);
94 }
95
96 for (Map.Entry<Token, EntryReference<Entry<?>, Entry<?>>> entry : tokenToReference.entrySet()) {
97 remapped.tokenToReference.put(result.getRemappedToken(entry.getKey()), entry.getValue());
98 }
99
100 return remapped;
101 }
102
103 public String getSource() {
104 return this.source;
105 }
106
107 public Token getToken(AstNode node) {
108
109 // get the text of the node
110 String name = "";
111 if (node instanceof Identifier) {
112 name = ((Identifier) node).getName();
113 }
114
115 // get a token for this node's region
116 Region region = node.getRegion();
117 if (region.getBeginLine() == 0 || region.getEndLine() == 0) {
118 // DEBUG
119 System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region));
120 return null;
121 }
122 Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source);
123 if (token.start == 0) {
124 // DEBUG
125 System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region));
126 return null;
127 }
128
129 if (node instanceof Identifier && name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()) {
130 TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null;
131 if (type != null) {
132 name = type.getName();
133 token.end = token.start + name.length();
134 }
135 }
136
137 // DEBUG
138 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) );
139
140 // Tokens can have $ in name, even for top-level classes
141 //if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) {
142 // // DEBUG
143 // System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name));
144 // return null;
145 //}
146
147 return token;
148 }
149
150 public void addReference(AstNode node, Entry<?> deobfEntry, Entry<?> deobfContext) {
151 Token token = getToken(node);
152 if (token != null) {
153 EntryReference<Entry<?>, Entry<?>> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext);
154 this.tokenToReference.put(token, deobfReference);
155 this.referenceToTokens.put(deobfReference, token);
156 }
157 }
158
159 public void addDeclaration(AstNode node, Entry<?> deobfEntry) {
160 Token token = getToken(node);
161 if (token != null) {
162 EntryReference<Entry<?>, Entry<?>> reference = new EntryReference<>(deobfEntry, token.text);
163 this.tokenToReference.put(token, reference);
164 this.referenceToTokens.put(reference, token);
165 this.declarationToToken.put(deobfEntry, token);
166 }
167 }
168
169 public Token getReferenceToken(int pos) {
170 Token token = this.tokenToReference.floorKey(new Token(pos, pos, null));
171 if (token != null && token.contains(pos)) {
172 return token;
173 }
174 return null;
175 }
176
177 public Collection<Token> getReferenceTokens(EntryReference<Entry<?>, Entry<?>> deobfReference) {
178 return this.referenceToTokens.get(deobfReference);
179 }
180
181 @Nullable
182 public EntryReference<Entry<?>, Entry<?>> getReference(Token token) {
183 if (token == null) {
184 return null;
185 }
186 return this.tokenToReference.get(token);
187 }
188
189 public Iterable<Token> referenceTokens() {
190 return this.tokenToReference.keySet();
191 }
192
193 public Iterable<Token> declarationTokens() {
194 return this.declarationToToken.values();
195 }
196
197 public Iterable<Entry<?>> declarations() {
198 return this.declarationToToken.keySet();
199 }
200
201 public Token getDeclarationToken(Entry<?> entry) {
202 return this.declarationToToken.get(entry);
203 }
204
205 public int getLineNumber(int pos) {
206 // line number is 1-based
207 int line = 0;
208 for (Integer offset : this.lineOffsets) {
209 if (offset > pos) {
210 break;
211 }
212 line++;
213 }
214 return line;
215 }
216
217 public int getColumnNumber(int pos) {
218 // column number is 1-based
219 return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1;
220 }
221
222 private int toPos(int line, int col) {
223 // line and col are 1-based
224 return this.lineOffsets.get(line - 1) + col - 1;
225 }
226
227 public void resolveReferences(EntryResolver resolver) {
228 // resolve all the classes in the source references
229 for (Token token : Lists.newArrayList(referenceToTokens.values())) {
230 EntryReference<Entry<?>, Entry<?>> reference = tokenToReference.get(token);
231 EntryReference<Entry<?>, Entry<?>> resolvedReference = resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST);
232
233 // replace the reference
234 tokenToReference.replace(token, resolvedReference);
235
236 Collection<Token> tokens = referenceToTokens.removeAll(reference);
237 referenceToTokens.putAll(resolvedReference, tokens);
238 }
239 }
240}