diff options
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis')
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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import com.google.common.base.Function; | ||
| 4 | import com.google.common.base.Strings; | ||
| 5 | import com.strobel.assembler.metadata.ParameterDefinition; | ||
| 6 | import com.strobel.decompiler.languages.java.ast.*; | ||
| 7 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | ||
| 8 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 9 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 10 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 11 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 12 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 14 | |||
| 15 | import java.util.ArrayList; | ||
| 16 | import java.util.Collections; | ||
| 17 | import java.util.List; | ||
| 18 | import java.util.Objects; | ||
| 19 | import java.util.stream.Stream; | ||
| 20 | |||
| 21 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | 1 | package cuchaz.enigma.analysis; |
| 2 | 2 | ||
| 3 | import com.strobel.core.Pair; | ||
| 4 | import cuchaz.enigma.api.EnigmaPlugin; | 3 | import cuchaz.enigma.api.EnigmaPlugin; |
| 5 | import cuchaz.enigma.api.EnigmaPluginContext; | 4 | import cuchaz.enigma.api.EnigmaPluginContext; |
| 6 | import cuchaz.enigma.api.service.JarIndexerService; | 5 | import cuchaz.enigma.api.service.JarIndexerService; |
| 7 | import cuchaz.enigma.api.service.NameProposalService; | 6 | import cuchaz.enigma.api.service.NameProposalService; |
| 8 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | 7 | import cuchaz.enigma.source.DecompilerService; |
| 8 | import cuchaz.enigma.source.Decompilers; | ||
| 9 | import cuchaz.enigma.source.procyon.ProcyonDecompiler; | ||
| 9 | import cuchaz.enigma.translation.representation.TypeDescriptor; | 10 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 10 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 11 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 11 | import cuchaz.enigma.translation.representation.entry.Entry; | 12 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 12 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | 13 | import cuchaz.enigma.translation.representation.entry.FieldEntry; |
| 13 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | 14 | import cuchaz.enigma.utils.Pair; |
| 14 | import org.objectweb.asm.ClassReader; | 15 | import org.objectweb.asm.ClassReader; |
| 15 | import org.objectweb.asm.ClassVisitor; | 16 | import org.objectweb.asm.ClassVisitor; |
| 16 | import org.objectweb.asm.FieldVisitor; | 17 | import org.objectweb.asm.FieldVisitor; |
| @@ -34,7 +35,6 @@ import java.util.List; | |||
| 34 | import java.util.Map; | 35 | import java.util.Map; |
| 35 | import java.util.Optional; | 36 | import java.util.Optional; |
| 36 | import java.util.Set; | 37 | import java.util.Set; |
| 37 | import java.util.function.UnaryOperator; | ||
| 38 | 38 | ||
| 39 | public final class BuiltinPlugin implements EnigmaPlugin { | 39 | public 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; | |||
| 3 | import com.google.common.cache.Cache; | 3 | import com.google.common.cache.Cache; |
| 4 | import com.google.common.cache.CacheBuilder; | 4 | import com.google.common.cache.CacheBuilder; |
| 5 | import com.google.common.collect.ImmutableSet; | 5 | import com.google.common.collect.ImmutableSet; |
| 6 | import cuchaz.enigma.CompiledSource; | 6 | import cuchaz.enigma.ClassProvider; |
| 7 | import cuchaz.enigma.ProgressListener; | 7 | import cuchaz.enigma.ProgressListener; |
| 8 | import cuchaz.enigma.analysis.index.JarIndex; | 8 | import cuchaz.enigma.analysis.index.JarIndex; |
| 9 | import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; | 9 | import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; |
| @@ -22,7 +22,7 @@ import java.util.concurrent.ExecutionException; | |||
| 22 | import java.util.concurrent.TimeUnit; | 22 | import java.util.concurrent.TimeUnit; |
| 23 | import java.util.function.Supplier; | 23 | import java.util.function.Supplier; |
| 24 | 24 | ||
| 25 | public final class ClassCache implements AutoCloseable, CompiledSource { | 25 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import com.strobel.decompiler.languages.java.ast.AstNode; | ||
| 4 | import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; | ||
| 5 | import com.strobel.decompiler.languages.java.ast.ImportDeclaration; | ||
| 6 | import com.strobel.decompiler.languages.java.ast.PackageDeclaration; | ||
| 7 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | ||
| 8 | |||
| 9 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import com.strobel.decompiler.languages.java.ast.*; | ||
| 4 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | ||
| 5 | |||
| 6 | import javax.lang.model.element.Modifier; | ||
| 7 | |||
| 8 | public 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 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Lists; | ||
| 16 | import com.google.common.collect.Maps; | ||
| 17 | import com.google.common.collect.Multimap; | ||
| 18 | import com.strobel.decompiler.languages.Region; | ||
| 19 | import com.strobel.decompiler.languages.java.ast.AstNode; | ||
| 20 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 21 | import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; | ||
| 22 | import com.strobel.decompiler.languages.java.ast.Identifier; | ||
| 23 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; | ||
| 24 | import cuchaz.enigma.gui.SourceRemapper; | ||
| 25 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 26 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 27 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 28 | |||
| 29 | import javax.annotation.Nullable; | ||
| 30 | import java.util.Collection; | ||
| 31 | import java.util.List; | ||
| 32 | import java.util.Map; | ||
| 33 | import java.util.TreeMap; | ||
| 34 | import java.util.regex.Pattern; | ||
| 35 | import java.util.stream.Collectors; | ||
| 36 | |||
| 37 | public 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 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.strobel.assembler.metadata.FieldDefinition; | ||
| 15 | import com.strobel.assembler.metadata.MethodDefinition; | ||
| 16 | import com.strobel.assembler.metadata.TypeDefinition; | ||
| 17 | import com.strobel.assembler.metadata.TypeReference; | ||
| 18 | import com.strobel.decompiler.languages.TextLocation; | ||
| 19 | import com.strobel.decompiler.languages.java.ast.*; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 21 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; | ||
| 23 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 24 | |||
| 25 | public 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 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | import com.strobel.assembler.metadata.*; | ||
| 17 | import com.strobel.decompiler.ast.Variable; | ||
| 18 | import com.strobel.decompiler.languages.TextLocation; | ||
| 19 | import com.strobel.decompiler.languages.java.ast.*; | ||
| 20 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 21 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 23 | |||
| 24 | import java.lang.Error; | ||
| 25 | import java.util.HashMap; | ||
| 26 | import java.util.Map; | ||
| 27 | |||
| 28 | public 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 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.strobel.assembler.metadata.TypeDefinition; | ||
| 15 | import com.strobel.decompiler.languages.java.ast.AstNode; | ||
| 16 | import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; | ||
| 17 | import com.strobel.decompiler.languages.java.ast.Keys; | ||
| 18 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 20 | |||
| 21 | public 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 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.strobel.componentmodel.Key; | ||
| 15 | import com.strobel.decompiler.languages.java.ast.*; | ||
| 16 | import com.strobel.decompiler.patterns.Pattern; | ||
| 17 | |||
| 18 | import java.io.*; | ||
| 19 | import java.nio.charset.Charset; | ||
| 20 | |||
| 21 | public 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 | } | ||