summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/AddJavadocsAstTransform.java135
-rw-r--r--src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java14
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassCache.java4
-rw-r--r--src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java33
-rw-r--r--src/main/java/cuchaz/enigma/analysis/DropVarModifiersAstTransform.java37
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java240
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java96
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java216
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java38
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Token.java6
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java93
11 files changed, 14 insertions, 898 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/AddJavadocsAstTransform.java b/src/main/java/cuchaz/enigma/analysis/AddJavadocsAstTransform.java
deleted file mode 100644
index 17ae63d..0000000
--- a/src/main/java/cuchaz/enigma/analysis/AddJavadocsAstTransform.java
+++ /dev/null
@@ -1,135 +0,0 @@
1package cuchaz.enigma.analysis;
2
3import com.google.common.base.Function;
4import com.google.common.base.Strings;
5import com.strobel.assembler.metadata.ParameterDefinition;
6import com.strobel.decompiler.languages.java.ast.*;
7import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
8import cuchaz.enigma.translation.mapping.EntryMapping;
9import cuchaz.enigma.translation.mapping.EntryRemapper;
10import cuchaz.enigma.translation.mapping.EntryResolver;
11import cuchaz.enigma.translation.mapping.ResolutionStrategy;
12import cuchaz.enigma.translation.representation.TypeDescriptor;
13import cuchaz.enigma.translation.representation.entry.*;
14
15import java.util.ArrayList;
16import java.util.Collections;
17import java.util.List;
18import java.util.Objects;
19import java.util.stream.Stream;
20
21public final class AddJavadocsAstTransform implements IAstTransform {
22
23 private final EntryRemapper remapper;
24
25 public AddJavadocsAstTransform(EntryRemapper remapper) {
26 this.remapper = remapper;
27 }
28
29 @Override
30 public void run(AstNode compilationUnit) {
31 compilationUnit.acceptVisitor(new Visitor(remapper), null);
32 }
33
34 static class Visitor extends DepthFirstAstVisitor<Void, Void> {
35
36 private final EntryRemapper remapper;
37
38 Visitor(EntryRemapper remapper) {
39 this.remapper = remapper;
40 }
41
42 private <T extends AstNode> void addDoc(T node, Function<T, Entry<?>> retriever) {
43 final Comment[] comments = getComments(node, retriever);
44 if (comments != null) {
45 node.insertChildrenBefore(node.getFirstChild(), Roles.COMMENT, comments);
46 }
47 }
48
49 private <T extends AstNode> Comment[] getComments(T node, Function<T, Entry<?>> retriever) {
50 final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node));
51 final String docs = mapping == null ? null : Strings.emptyToNull(mapping.getJavadoc());
52 return docs == null ? null : Stream.of(docs.split("\\R")).map(st -> new Comment(st,
53 CommentType.Documentation)).toArray(Comment[]::new);
54 }
55
56 private Comment[] getParameterComments(ParameterDeclaration node, Function<ParameterDeclaration, Entry<?>> retriever) {
57 final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node));
58 final Comment[] ret = getComments(node, retriever);
59 if (ret != null) {
60 final String paramPrefix = "@param " + mapping.getTargetName() + " ";
61 final String indent = Strings.repeat(" ", paramPrefix.length());
62 ret[0].setContent(paramPrefix + ret[0].getContent());
63 for (int i = 1; i < ret.length; i++) {
64 ret[i].setContent(indent + ret[i].getContent());
65 }
66 }
67 return ret;
68 }
69
70 private void visitMethod(AstNode node) {
71 final MethodDefEntry methodDefEntry = MethodDefEntry.parse(node.getUserData(Keys.METHOD_DEFINITION));
72 final Comment[] baseComments = getComments(node, $ -> methodDefEntry);
73 List<Comment> comments = new ArrayList<>();
74 if (baseComments != null)
75 Collections.addAll(comments, baseComments);
76
77 for (ParameterDeclaration dec : node.getChildrenByRole(Roles.PARAMETER)) {
78 ParameterDefinition def = dec.getUserData(Keys.PARAMETER_DEFINITION);
79 final Comment[] paramComments = getParameterComments(dec, $ -> new LocalVariableDefEntry(methodDefEntry, def.getSlot(), def.getName(),
80 true,
81 TypeDescriptor.parse(def.getParameterType()), null));
82 if (paramComments != null)
83 Collections.addAll(comments, paramComments);
84 }
85
86 if (!comments.isEmpty()) {
87 if (remapper.getObfResolver().resolveEntry(methodDefEntry, ResolutionStrategy.RESOLVE_ROOT).stream().noneMatch(e -> Objects.equals(e, methodDefEntry))) {
88 comments.add(0, new Comment("{@inheritDoc}", CommentType.Documentation));
89 }
90 final AstNode oldFirst = node.getFirstChild();
91 for (Comment comment : comments) {
92 node.insertChildBefore(oldFirst, comment, Roles.COMMENT);
93 }
94 }
95 }
96
97 @Override
98 protected Void visitChildren(AstNode node, Void data) {
99 for (final AstNode child : node.getChildren()) {
100 child.acceptVisitor(this, data);
101 }
102 return null;
103 }
104
105 @Override
106 public Void visitMethodDeclaration(MethodDeclaration node, Void data) {
107 visitMethod(node);
108 return super.visitMethodDeclaration(node, data);
109 }
110
111 @Override
112 public Void visitConstructorDeclaration(ConstructorDeclaration node, Void data) {
113 visitMethod(node);
114 return super.visitConstructorDeclaration(node, data);
115 }
116
117 @Override
118 public Void visitFieldDeclaration(FieldDeclaration node, Void data) {
119 addDoc(node, dec -> FieldDefEntry.parse(dec.getUserData(Keys.FIELD_DEFINITION)));
120 return super.visitFieldDeclaration(node, data);
121 }
122
123 @Override
124 public Void visitTypeDeclaration(TypeDeclaration node, Void data) {
125 addDoc(node, dec -> ClassDefEntry.parse(dec.getUserData(Keys.TYPE_DEFINITION)));
126 return super.visitTypeDeclaration(node, data);
127 }
128
129 @Override
130 public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void data) {
131 addDoc(node, dec -> FieldDefEntry.parse(dec.getUserData(Keys.FIELD_DEFINITION)));
132 return super.visitEnumValueDeclaration(node, data);
133 }
134 }
135}
diff --git a/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java b/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java
index 12ef709..fddd9a8 100644
--- a/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java
+++ b/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java
@@ -1,16 +1,17 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.analysis;
2 2
3import com.strobel.core.Pair;
4import cuchaz.enigma.api.EnigmaPlugin; 3import cuchaz.enigma.api.EnigmaPlugin;
5import cuchaz.enigma.api.EnigmaPluginContext; 4import cuchaz.enigma.api.EnigmaPluginContext;
6import cuchaz.enigma.api.service.JarIndexerService; 5import cuchaz.enigma.api.service.JarIndexerService;
7import cuchaz.enigma.api.service.NameProposalService; 6import cuchaz.enigma.api.service.NameProposalService;
8import cuchaz.enigma.translation.mapping.ResolutionStrategy; 7import cuchaz.enigma.source.DecompilerService;
8import cuchaz.enigma.source.Decompilers;
9import cuchaz.enigma.source.procyon.ProcyonDecompiler;
9import cuchaz.enigma.translation.representation.TypeDescriptor; 10import cuchaz.enigma.translation.representation.TypeDescriptor;
10import cuchaz.enigma.translation.representation.entry.ClassEntry; 11import cuchaz.enigma.translation.representation.entry.ClassEntry;
11import cuchaz.enigma.translation.representation.entry.Entry; 12import cuchaz.enigma.translation.representation.entry.Entry;
12import cuchaz.enigma.translation.representation.entry.FieldEntry; 13import cuchaz.enigma.translation.representation.entry.FieldEntry;
13import cuchaz.enigma.translation.representation.entry.MethodEntry; 14import cuchaz.enigma.utils.Pair;
14import org.objectweb.asm.ClassReader; 15import org.objectweb.asm.ClassReader;
15import org.objectweb.asm.ClassVisitor; 16import org.objectweb.asm.ClassVisitor;
16import org.objectweb.asm.FieldVisitor; 17import org.objectweb.asm.FieldVisitor;
@@ -34,7 +35,6 @@ import java.util.List;
34import java.util.Map; 35import java.util.Map;
35import java.util.Optional; 36import java.util.Optional;
36import java.util.Set; 37import java.util.Set;
37import java.util.function.UnaryOperator;
38 38
39public final class BuiltinPlugin implements EnigmaPlugin { 39public final class BuiltinPlugin implements EnigmaPlugin {
40 40
@@ -44,6 +44,7 @@ public final class BuiltinPlugin implements EnigmaPlugin {
44 @Override 44 @Override
45 public void init(EnigmaPluginContext ctx) { 45 public void init(EnigmaPluginContext ctx) {
46 registerEnumNamingService(ctx); 46 registerEnumNamingService(ctx);
47 registerDecompilerServices(ctx);
47 } 48 }
48 49
49 private void registerEnumNamingService(EnigmaPluginContext ctx) { 50 private void registerEnumNamingService(EnigmaPluginContext ctx) {
@@ -54,6 +55,11 @@ public final class BuiltinPlugin implements EnigmaPlugin {
54 ctx.registerService("enigma:enum_name_proposer", NameProposalService.TYPE, ctx1 -> (obfEntry, remapper) -> Optional.ofNullable(names.get(obfEntry))); 55 ctx.registerService("enigma:enum_name_proposer", NameProposalService.TYPE, ctx1 -> (obfEntry, remapper) -> Optional.ofNullable(names.get(obfEntry)));
55 } 56 }
56 57
58 private void registerDecompilerServices(EnigmaPluginContext ctx) {
59 ctx.registerService("enigma:procyon", DecompilerService.TYPE, ctx1 -> Decompilers.PROCYON);
60 ctx.registerService("enigma:cfr", DecompilerService.TYPE, ctx1 -> Decompilers.CFR);
61 }
62
57 private static final class EnumFieldNameFindingVisitor extends ClassVisitor { 63 private static final class EnumFieldNameFindingVisitor extends ClassVisitor {
58 64
59 private ClassEntry clazz; 65 private ClassEntry clazz;
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassCache.java b/src/main/java/cuchaz/enigma/analysis/ClassCache.java
index 8453df1..d97b204 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassCache.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassCache.java
@@ -3,7 +3,7 @@ package cuchaz.enigma.analysis;
3import com.google.common.cache.Cache; 3import com.google.common.cache.Cache;
4import com.google.common.cache.CacheBuilder; 4import com.google.common.cache.CacheBuilder;
5import com.google.common.collect.ImmutableSet; 5import com.google.common.collect.ImmutableSet;
6import cuchaz.enigma.CompiledSource; 6import cuchaz.enigma.ClassProvider;
7import cuchaz.enigma.ProgressListener; 7import cuchaz.enigma.ProgressListener;
8import cuchaz.enigma.analysis.index.JarIndex; 8import cuchaz.enigma.analysis.index.JarIndex;
9import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; 9import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor;
@@ -22,7 +22,7 @@ import java.util.concurrent.ExecutionException;
22import java.util.concurrent.TimeUnit; 22import java.util.concurrent.TimeUnit;
23import java.util.function.Supplier; 23import java.util.function.Supplier;
24 24
25public final class ClassCache implements AutoCloseable, CompiledSource { 25public final class ClassCache implements AutoCloseable, ClassProvider {
26 private final FileSystem fileSystem; 26 private final FileSystem fileSystem;
27 private final ImmutableSet<String> classNames; 27 private final ImmutableSet<String> classNames;
28 28
diff --git a/src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java b/src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java
deleted file mode 100644
index 991e91d..0000000
--- a/src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java
+++ /dev/null
@@ -1,33 +0,0 @@
1package cuchaz.enigma.analysis;
2
3import com.strobel.decompiler.languages.java.ast.AstNode;
4import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
5import com.strobel.decompiler.languages.java.ast.ImportDeclaration;
6import com.strobel.decompiler.languages.java.ast.PackageDeclaration;
7import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
8
9public final class DropImportAstTransform implements IAstTransform {
10 public static final DropImportAstTransform INSTANCE = new DropImportAstTransform();
11
12 private DropImportAstTransform() {
13 }
14
15 @Override
16 public void run(AstNode compilationUnit) {
17 compilationUnit.acceptVisitor(new Visitor(), null);
18 }
19
20 static class Visitor extends DepthFirstAstVisitor<Void, Void> {
21 @Override
22 public Void visitPackageDeclaration(PackageDeclaration node, Void data) {
23 node.remove();
24 return null;
25 }
26
27 @Override
28 public Void visitImportDeclaration(ImportDeclaration node, Void data) {
29 node.remove();
30 return null;
31 }
32 }
33}
diff --git a/src/main/java/cuchaz/enigma/analysis/DropVarModifiersAstTransform.java b/src/main/java/cuchaz/enigma/analysis/DropVarModifiersAstTransform.java
deleted file mode 100644
index 0be5891..0000000
--- a/src/main/java/cuchaz/enigma/analysis/DropVarModifiersAstTransform.java
+++ /dev/null
@@ -1,37 +0,0 @@
1package cuchaz.enigma.analysis;
2
3import com.strobel.decompiler.languages.java.ast.*;
4import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
5
6import javax.lang.model.element.Modifier;
7
8public final class DropVarModifiersAstTransform implements IAstTransform {
9 public static final DropVarModifiersAstTransform INSTANCE = new DropVarModifiersAstTransform();
10
11 private DropVarModifiersAstTransform() {
12 }
13
14 @Override
15 public void run(AstNode compilationUnit) {
16 compilationUnit.acceptVisitor(new Visitor(), null);
17 }
18
19 static class Visitor extends DepthFirstAstVisitor<Void, Void> {
20 @Override
21 public Void visitParameterDeclaration(ParameterDeclaration node, Void data) {
22 for (JavaModifierToken modifierToken : node.getChildrenByRole(EntityDeclaration.MODIFIER_ROLE)) {
23 if (modifierToken.getModifier() == Modifier.FINAL) {
24 modifierToken.remove();
25 }
26 }
27
28 return null;
29 }
30
31 @Override
32 public Void visitVariableDeclaration(VariableDeclarationStatement node, Void data) {
33 node.removeModifier(Modifier.FINAL);
34 return null;
35 }
36 }
37}
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}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
deleted file mode 100644
index 2a72cb1..0000000
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
+++ /dev/null
@@ -1,96 +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.strobel.assembler.metadata.FieldDefinition;
15import com.strobel.assembler.metadata.MethodDefinition;
16import com.strobel.assembler.metadata.TypeDefinition;
17import com.strobel.assembler.metadata.TypeReference;
18import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
21import cuchaz.enigma.translation.representation.entry.ClassEntry;
22import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
23import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
24
25public class SourceIndexClassVisitor extends SourceIndexVisitor {
26 private ClassDefEntry classEntry;
27
28 public SourceIndexClassVisitor(ClassDefEntry classEntry) {
29 this.classEntry = classEntry;
30 }
31
32 @Override
33 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
34 // is this this class, or a subtype?
35 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
36 ClassDefEntry classEntry = ClassDefEntry.parse(def);
37 if (!classEntry.equals(this.classEntry)) {
38 // it's a subtype, recurse
39 index.addDeclaration(node.getNameToken(), classEntry);
40 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
41 }
42
43 return visitChildren(node, index);
44 }
45
46 @Override
47 public Void visitSimpleType(SimpleType node, SourceIndex index) {
48 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
49 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
50 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
51 index.addReference(node.getIdentifierToken(), classEntry, this.classEntry);
52 }
53
54 return visitChildren(node, index);
55 }
56
57 @Override
58 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
59 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
60 MethodDefEntry methodEntry = MethodDefEntry.parse(def);
61 AstNode tokenNode = node.getNameToken();
62 if (methodEntry.isConstructor() && methodEntry.getName().equals("<clinit>")) {
63 // for static initializers, check elsewhere for the token node
64 tokenNode = node.getModifiers().firstOrNullObject();
65 }
66 index.addDeclaration(tokenNode, methodEntry);
67 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index);
68 }
69
70 @Override
71 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
72 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
73 MethodDefEntry methodEntry = MethodDefEntry.parse(def);
74 index.addDeclaration(node.getNameToken(), methodEntry);
75 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index);
76 }
77
78 @Override
79 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
80 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
81 FieldDefEntry fieldEntry = FieldDefEntry.parse(def);
82 assert (node.getVariables().size() == 1);
83 VariableInitializer variable = node.getVariables().firstOrNullObject();
84 index.addDeclaration(variable.getNameToken(), fieldEntry);
85 return visitChildren(node, index);
86 }
87
88 @Override
89 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
90 // treat enum declarations as field declarations
91 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
92 FieldDefEntry fieldEntry = FieldDefEntry.parse(def);
93 index.addDeclaration(node.getNameToken(), fieldEntry);
94 return visitChildren(node, index);
95 }
96}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
deleted file mode 100644
index dfe58ba..0000000
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
+++ /dev/null
@@ -1,216 +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.Multimap;
16import com.strobel.assembler.metadata.*;
17import com.strobel.decompiler.ast.Variable;
18import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.translation.representation.MethodDescriptor;
21import cuchaz.enigma.translation.representation.TypeDescriptor;
22import cuchaz.enigma.translation.representation.entry.*;
23
24import java.lang.Error;
25import java.util.HashMap;
26import java.util.Map;
27
28public class SourceIndexMethodVisitor extends SourceIndexVisitor {
29 private final MethodDefEntry methodEntry;
30
31 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create();
32 private Map<String, Entry<?>> identifierEntryCache = new HashMap<>();
33
34 public SourceIndexMethodVisitor(MethodDefEntry methodEntry) {
35 this.methodEntry = methodEntry;
36 }
37
38 @Override
39 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) {
40 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
41
42 // get the behavior entry
43 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
44 MethodEntry methodEntry = null;
45 if (ref instanceof MethodReference) {
46 methodEntry = new MethodEntry(classEntry, ref.getName(), new MethodDescriptor(ref.getErasedSignature()));
47 }
48 if (methodEntry != null) {
49 // get the node for the token
50 AstNode tokenNode = null;
51 if (node.getTarget() instanceof MemberReferenceExpression) {
52 tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken();
53 } else if (node.getTarget() instanceof SuperReferenceExpression) {
54 tokenNode = node.getTarget();
55 } else if (node.getTarget() instanceof ThisReferenceExpression) {
56 tokenNode = node.getTarget();
57 }
58 if (tokenNode != null) {
59 index.addReference(tokenNode, methodEntry, this.methodEntry);
60 }
61 }
62
63 // Check for identifier
64 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression)
65 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index));
66 return visitChildren(node, index);
67 }
68
69 @Override
70 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) {
71 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
72 if (ref instanceof FieldReference) {
73 // make sure this is actually a field
74 String erasedSignature = ref.getErasedSignature();
75 if (erasedSignature.indexOf('(') >= 0) {
76 throw new Error("Expected a field here! got " + ref);
77 }
78
79 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
80 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(erasedSignature));
81 index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry);
82 }
83
84 return visitChildren(node, index);
85 }
86
87 @Override
88 public Void visitSimpleType(SimpleType node, SourceIndex index) {
89 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
90 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
91 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
92 index.addReference(node.getIdentifierToken(), classEntry, this.methodEntry);
93 }
94
95 return visitChildren(node, index);
96 }
97
98 @Override
99 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
100 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION);
101 int parameterIndex = def.getSlot();
102
103 if (parameterIndex >= 0) {
104 MethodDefEntry ownerMethod = methodEntry;
105 if (def.getMethod() instanceof MethodDefinition) {
106 ownerMethod = MethodDefEntry.parse((MethodDefinition) def.getMethod());
107 }
108
109 TypeDescriptor parameterType = TypeDescriptor.parse(def.getParameterType());
110 LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(ownerMethod, parameterIndex, node.getName(), true, parameterType, null);
111 Identifier identifier = node.getNameToken();
112 // cache the argument entry and the identifier
113 identifierEntryCache.put(identifier.getName(), localVariableEntry);
114 index.addDeclaration(identifier, localVariableEntry);
115 }
116
117 return visitChildren(node, index);
118 }
119
120 @Override
121 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
122 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
123 if (ref != null) {
124 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
125 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature()));
126 index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry);
127 } else
128 this.checkIdentifier(node, index);
129 return visitChildren(node, index);
130 }
131
132 private void checkIdentifier(IdentifierExpression node, SourceIndex index) {
133 if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token!
134 index.addDeclaration(node.getIdentifierToken(), identifierEntryCache.get(node.getIdentifier()));
135 else
136 unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it!
137 }
138
139 private void addDeclarationToUnmatched(String key, SourceIndex index) {
140 Entry<?> entry = identifierEntryCache.get(key);
141
142 // This cannot happened in theory
143 if (entry == null)
144 return;
145 for (Identifier identifier : unmatchedIdentifier.get(key))
146 index.addDeclaration(identifier, entry);
147 unmatchedIdentifier.removeAll(key);
148 }
149
150 @Override
151 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
152 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
153 if (ref != null && node.getType() instanceof SimpleType) {
154 SimpleType simpleTypeNode = (SimpleType) node.getType();
155 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
156 MethodEntry constructorEntry = new MethodEntry(classEntry, "<init>", new MethodDescriptor(ref.getErasedSignature()));
157 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.methodEntry);
158 }
159
160 return visitChildren(node, index);
161 }
162
163 @Override
164 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) {
165 AstNodeCollection<VariableInitializer> variables = node.getVariables();
166
167 // Single assignation
168 if (variables.size() == 1) {
169 VariableInitializer initializer = variables.firstOrNullObject();
170 if (initializer != null && node.getType() instanceof SimpleType) {
171 Identifier identifier = initializer.getNameToken();
172 Variable variable = initializer.getUserData(Keys.VARIABLE);
173 if (variable != null) {
174 VariableDefinition originalVariable = variable.getOriginalVariable();
175 if (originalVariable != null) {
176 int variableIndex = originalVariable.getSlot();
177 if (variableIndex >= 0) {
178 MethodDefEntry ownerMethod = MethodDefEntry.parse(originalVariable.getDeclaringMethod());
179 TypeDescriptor variableType = TypeDescriptor.parse(originalVariable.getVariableType());
180 LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(ownerMethod, variableIndex, initializer.getName(), false, variableType, null);
181 identifierEntryCache.put(identifier.getName(), localVariableEntry);
182 addDeclarationToUnmatched(identifier.getName(), index);
183 index.addDeclaration(identifier, localVariableEntry);
184 }
185 }
186 }
187 }
188 }
189 return visitChildren(node, index);
190 }
191
192 @Override
193 public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) {
194 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
195
196 if (ref instanceof MethodReference) {
197 // get the behavior entry
198 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
199 MethodEntry methodEntry = new MethodEntry(classEntry, ref.getName(), new MethodDescriptor(ref.getErasedSignature()));
200
201 // get the node for the token
202 AstNode methodNameToken = node.getMethodNameToken();
203 AstNode targetToken = node.getTarget();
204
205 if (methodNameToken != null) {
206 index.addReference(methodNameToken, methodEntry, this.methodEntry);
207 }
208
209 if (targetToken != null && !(targetToken instanceof ThisReferenceExpression)) {
210 index.addReference(targetToken, methodEntry.getParent(), this.methodEntry);
211 }
212 }
213
214 return visitChildren(node, index);
215 }
216}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
deleted file mode 100644
index 8bd00a8..0000000
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
+++ /dev/null
@@ -1,38 +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.strobel.assembler.metadata.TypeDefinition;
15import com.strobel.decompiler.languages.java.ast.AstNode;
16import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
17import com.strobel.decompiler.languages.java.ast.Keys;
18import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
19import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
20
21public class SourceIndexVisitor extends DepthFirstAstVisitor<SourceIndex, Void> {
22 @Override
23 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
24 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
25 ClassDefEntry classEntry = ClassDefEntry.parse(def);
26 index.addDeclaration(node.getNameToken(), classEntry);
27
28 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
29 }
30
31 @Override
32 protected Void visitChildren(AstNode node, SourceIndex index) {
33 for (final AstNode child : node.getChildren()) {
34 child.acceptVisitor(this, index);
35 }
36 return null;
37 }
38}
diff --git a/src/main/java/cuchaz/enigma/analysis/Token.java b/src/main/java/cuchaz/enigma/analysis/Token.java
index 12e0aa6..f0155e5 100644
--- a/src/main/java/cuchaz/enigma/analysis/Token.java
+++ b/src/main/java/cuchaz/enigma/analysis/Token.java
@@ -17,12 +17,10 @@ public class Token implements Comparable<Token> {
17 public int end; 17 public int end;
18 public String text; 18 public String text;
19 19
20 public Token(int start, int end, String source) { 20 public Token(int start, int end, String text) {
21 this.start = start; 21 this.start = start;
22 this.end = end; 22 this.end = end;
23 if (source != null) { 23 this.text = text;
24 this.text = source.substring(start, end);
25 }
26 } 24 }
27 25
28 public int getRenameOffset(String to) { 26 public int getRenameOffset(String to) {
diff --git a/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java
deleted file mode 100644
index c85d97a..0000000
--- a/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java
+++ /dev/null
@@ -1,93 +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.strobel.componentmodel.Key;
15import com.strobel.decompiler.languages.java.ast.*;
16import com.strobel.decompiler.patterns.Pattern;
17
18import java.io.*;
19import java.nio.charset.Charset;
20
21public class TreeDumpVisitor extends DepthFirstAstVisitor<Void, Void> {
22
23 private File file;
24 private Writer out;
25
26 public TreeDumpVisitor(File file) {
27 this.file = file;
28 }
29
30 @Override
31 public Void visitCompilationUnit(CompilationUnit node, Void ignored) {
32 try {
33 out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"));
34 visitChildren(node, ignored);
35 out.close();
36 return null;
37 } catch (IOException ex) {
38 throw new Error(ex);
39 }
40 }
41
42 @Override
43 protected Void visitChildren(AstNode node, Void ignored) {
44 // show the tree
45 try {
46 out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n");
47 } catch (IOException ex) {
48 throw new Error(ex);
49 }
50
51 // recurse
52 for (final AstNode child : node.getChildren()) {
53 child.acceptVisitor(this, ignored);
54 }
55 return null;
56 }
57
58 private String getText(AstNode node) {
59 if (node instanceof Identifier) {
60 return "\"" + ((Identifier) node).getName() + "\"";
61 }
62 return "";
63 }
64
65 private String dumpUserData(AstNode node) {
66 StringBuilder buf = new StringBuilder();
67 for (Key<?> key : Keys.ALL_KEYS) {
68 Object val = node.getUserData(key);
69 if (val != null) {
70 buf.append(String.format(" [%s=%s]", key, val));
71 }
72 }
73 return buf.toString();
74 }
75
76 private String getIndent(AstNode node) {
77 StringBuilder buf = new StringBuilder();
78 int depth = getDepth(node);
79 for (int i = 0; i < depth; i++) {
80 buf.append("\t");
81 }
82 return buf.toString();
83 }
84
85 private int getDepth(AstNode node) {
86 int depth = -1;
87 while (node != null) {
88 depth++;
89 node = node.getParent();
90 }
91 return depth;
92 }
93}