summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/gui/stats
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/gui/stats')
-rw-r--r--src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java188
-rw-r--r--src/main/java/cuchaz/enigma/gui/stats/StatsMember.java8
2 files changed, 196 insertions, 0 deletions
diff --git a/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java b/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java
new file mode 100644
index 0000000..485daf5
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java
@@ -0,0 +1,188 @@
1package cuchaz.enigma.gui.stats;
2
3import com.google.gson.GsonBuilder;
4import cuchaz.enigma.EnigmaProject;
5import cuchaz.enigma.ProgressListener;
6import cuchaz.enigma.analysis.index.EntryIndex;
7import cuchaz.enigma.api.service.NameProposalService;
8import cuchaz.enigma.api.service.ObfuscationTestService;
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.*;
16
17public class StatsGenerator {
18 private final EntryIndex entryIndex;
19 private final EntryRemapper mapper;
20 private final EntryResolver entryResolver;
21 private final ObfuscationTestService obfuscationTestService;
22 private final NameProposalService nameProposalService;
23
24 public StatsGenerator(EnigmaProject project) {
25 entryIndex = project.getJarIndex().getEntryIndex();
26 mapper = project.getMapper();
27 entryResolver = project.getJarIndex().getEntryResolver();
28 obfuscationTestService = project.getEnigma().getServices().get(ObfuscationTestService.TYPE).orElse(null);
29 nameProposalService = project.getEnigma().getServices().get(NameProposalService.TYPE).orElse(null);
30 }
31
32 public String generate(ProgressListener progress, Set<StatsMember> includedMembers) {
33 includedMembers = EnumSet.copyOf(includedMembers);
34 int totalWork = 0;
35
36 if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) {
37 totalWork += entryIndex.getMethods().size();
38 }
39
40 if (includedMembers.contains(StatsMember.FIELDS)) {
41 totalWork += entryIndex.getFields().size();
42 }
43
44 if (includedMembers.contains(StatsMember.CLASSES)) {
45 totalWork += entryIndex.getClasses().size();
46 }
47
48 progress.init(totalWork, "Generating stats");
49
50 Map<String, Integer> counts = new HashMap<>();
51
52 int numDone = 0;
53 if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) {
54 for (MethodEntry method : entryIndex.getMethods()) {
55 progress.step(numDone++, "Methods");
56 MethodEntry root = entryResolver
57 .resolveEntry(method, ResolutionStrategy.RESOLVE_ROOT)
58 .stream()
59 .findFirst()
60 .orElseThrow(AssertionError::new);
61
62 if (root == method && !((MethodDefEntry) method).getAccess().isSynthetic()) {
63 if (includedMembers.contains(StatsMember.METHODS)) {
64 update(counts, method);
65 }
66
67 if (includedMembers.contains(StatsMember.PARAMETERS)) {
68 int index = ((MethodDefEntry) method).getAccess().isStatic() ? 0 : 1;
69 for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) {
70 update(counts, new LocalVariableEntry(method, index, "", true));
71 index += argument.getSize();
72 }
73 }
74 }
75 }
76 }
77
78 if (includedMembers.contains(StatsMember.FIELDS)) {
79 for (FieldEntry field : entryIndex.getFields()) {
80 progress.step(numDone++, "Fields");
81 update(counts, field);
82 }
83 }
84
85 if (includedMembers.contains(StatsMember.CLASSES)) {
86 for (ClassEntry clazz : entryIndex.getClasses()) {
87 progress.step(numDone++, "Classes");
88 update(counts, clazz);
89 }
90 }
91
92 progress.step(-1, "Generating data");
93
94 Tree<Integer> tree = new Tree<>();
95
96 for (Map.Entry<String, Integer> entry : counts.entrySet()) {
97 if (entry.getKey().startsWith("com.mojang")) continue; // just a few unmapped names, no point in having a subsection
98 tree.getNode(entry.getKey()).value = entry.getValue();
99 }
100
101 tree.collapse(tree.root);
102 return new GsonBuilder().setPrettyPrinting().create().toJson(tree.root);
103 }
104
105 private void update(Map<String, Integer> counts, Entry<?> entry) {
106 if (isObfuscated(entry)) {
107 String parent = mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.');
108 counts.put(parent, counts.getOrDefault(parent, 0) + 1);
109 }
110 }
111
112 private boolean isObfuscated(Entry<?> entry) {
113 String name = entry.getName();
114
115 if (obfuscationTestService != null && obfuscationTestService.testDeobfuscated(entry)) {
116 return false;
117 }
118
119 if (nameProposalService != null && nameProposalService.proposeName(entry, mapper).isPresent()) {
120 return false;
121 }
122
123 String mappedName = mapper.deobfuscate(entry).getName();
124 if (mappedName != null && !mappedName.isEmpty() && !mappedName.equals(name)) {
125 return false;
126 }
127
128 return true;
129 }
130
131 private static class Tree<T> {
132 public final Node<T> root;
133 private final Map<String, Node<T>> nodes = new HashMap<>();
134
135 public static class Node<T> {
136 public String name;
137 public T value;
138 public List<Node<T>> children = new ArrayList<>();
139 private final transient Map<String, Node<T>> namedChildren = new HashMap<>();
140
141 public Node(String name, T value) {
142 this.name = name;
143 this.value = value;
144 }
145 }
146
147 public Tree() {
148 root = new Node<>("", null);
149 }
150
151 public Node<T> getNode(String name) {
152 Node<T> node = nodes.get(name);
153
154 if (node == null) {
155 node = root;
156
157 for (String part : name.split("\\.")) {
158 Node<T> child = node.namedChildren.get(part);
159
160 if (child == null) {
161 child = new Node<>(part, null);
162 node.namedChildren.put(part, child);
163 node.children.add(child);
164 }
165
166 node = child;
167 }
168
169 nodes.put(name, node);
170 }
171
172 return node;
173 }
174
175 public void collapse(Node<T> node) {
176 while (node.children.size() == 1) {
177 Node<T> child = node.children.get(0);
178 node.name = node.name.isEmpty() ? child.name : node.name + "." + child.name;
179 node.children = child.children;
180 node.value = child.value;
181 }
182
183 for (Node<T> child : node.children) {
184 collapse(child);
185 }
186 }
187 }
188}
diff --git a/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java b/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java
new file mode 100644
index 0000000..70b4f40
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java
@@ -0,0 +1,8 @@
1package cuchaz.enigma.gui.stats;
2
3public enum StatsMember {
4 METHODS,
5 FIELDS,
6 PARAMETERS,
7 CLASSES
8}