summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar 2xsaiko2021-03-25 14:08:23 +0100
committerGravatar GitHub2021-03-25 14:08:23 +0100
commit7fd4903f14458733a4cb7f1e554303c42c2f0de9 (patch)
tree9d4c2bbdf3a4b1673a106232db039787f1fa4f58
parentBump version (diff)
parentWIP full record support (diff)
downloadenigma-7fd4903f14458733a4cb7f1e554303c42c2f0de9.tar.gz
enigma-7fd4903f14458733a4cb7f1e554303c42c2f0de9.tar.xz
enigma-7fd4903f14458733a4cb7f1e554303c42c2f0de9.zip
Merge pull request #361 from modmuss50/records
Record support
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java10
-rw-r--r--enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java9
-rw-r--r--enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java30
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java57
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java45
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java1
-rw-r--r--enigma/src/main/resources/lang/en_us.json1
7 files changed, 148 insertions, 5 deletions
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java
index bab50df1..e4adadf1 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java
@@ -272,7 +272,15 @@ public final class UiConfig {
272 OptionalInt x = section.getInt(String.format("X %s", screenSize.width)); 272 OptionalInt x = section.getInt(String.format("X %s", screenSize.width));
273 OptionalInt y = section.getInt(String.format("Y %s", screenSize.height)); 273 OptionalInt y = section.getInt(String.format("Y %s", screenSize.height));
274 if (x.isPresent() && y.isPresent()) { 274 if (x.isPresent() && y.isPresent()) {
275 return new Point(x.getAsInt(), y.getAsInt()); 275 int ix = x.getAsInt();
276 int iy = y.getAsInt();
277
278 // Ensure that the position is on the screen.
279 if (ix < 0 || iy < 0 || ix > screenSize.width || iy > screenSize.height) {
280 return fallback;
281 }
282
283 return new Point(ix, iy);
276 } else { 284 } else {
277 return fallback; 285 return fallback;
278 } 286 }
diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
index e4c41d32..0b2ca9ae 100644
--- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
+++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
@@ -99,4 +99,13 @@ public class TranslationClassVisitor extends ClassVisitor {
99 AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, translatedDesc.toString(), visible); 99 AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, translatedDesc.toString(), visible);
100 return new TranslationAnnotationVisitor(translator, translatedDesc.getTypeEntry(), api, av); 100 return new TranslationAnnotationVisitor(translator, translatedDesc.getTypeEntry(), api, av);
101 } 101 }
102
103 @Override
104 public RecordComponentVisitor visitRecordComponent(String name, String desc, String signature) {
105 // Record component names are remapped via the field mapping.
106 FieldDefEntry entry = FieldDefEntry.parse(obfClassEntry, 0, name, desc, signature);
107 FieldDefEntry translatedEntry = translator.translate(entry);
108 RecordComponentVisitor fv = super.visitRecordComponent(translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString());
109 return new TranslationRecordComponentVisitor(translator, api, fv);
110 }
102} 111}
diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java
new file mode 100644
index 00000000..06fd22bb
--- /dev/null
+++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java
@@ -0,0 +1,30 @@
1package cuchaz.enigma.bytecode.translators;
2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.representation.TypeDescriptor;
5import org.objectweb.asm.AnnotationVisitor;
6import org.objectweb.asm.RecordComponentVisitor;
7import org.objectweb.asm.TypePath;
8
9public class TranslationRecordComponentVisitor extends RecordComponentVisitor {
10 private final Translator translator;
11
12 public TranslationRecordComponentVisitor(Translator translator, int api, RecordComponentVisitor rcv) {
13 super(api, rcv);
14 this.translator = translator;
15 }
16
17 @Override
18 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
19 TypeDescriptor typeDesc = translator.translate(new TypeDescriptor(desc));
20 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible);
21 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
22 }
23
24 @Override
25 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
26 TypeDescriptor typeDesc = translator.translate(new TypeDescriptor(desc));
27 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible);
28 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
29 }
30}
diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
index 2c6ec15a..d0734c5e 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
@@ -19,6 +19,9 @@ import org.benf.cfr.reader.bytecode.analysis.types.JavaTypeInstance;
19import org.benf.cfr.reader.bytecode.analysis.types.MethodPrototype; 19import org.benf.cfr.reader.bytecode.analysis.types.MethodPrototype;
20import org.benf.cfr.reader.bytecode.analysis.types.RawJavaType; 20import org.benf.cfr.reader.bytecode.analysis.types.RawJavaType;
21import org.benf.cfr.reader.bytecode.analysis.variables.NamedVariable; 21import org.benf.cfr.reader.bytecode.analysis.variables.NamedVariable;
22import org.benf.cfr.reader.entities.AccessFlag;
23import org.benf.cfr.reader.entities.ClassFile;
24import org.benf.cfr.reader.entities.ClassFileField;
22import org.benf.cfr.reader.entities.Field; 25import org.benf.cfr.reader.entities.Field;
23import org.benf.cfr.reader.state.TypeUsageInformation; 26import org.benf.cfr.reader.state.TypeUsageInformation;
24import org.benf.cfr.reader.util.getopt.Options; 27import org.benf.cfr.reader.util.getopt.Options;
@@ -33,6 +36,7 @@ import java.util.ArrayList;
33import java.util.Arrays; 36import java.util.Arrays;
34import java.util.Collection; 37import java.util.Collection;
35import java.util.HashMap; 38import java.util.HashMap;
39import java.util.LinkedList;
36import java.util.List; 40import java.util.List;
37import java.util.Map; 41import java.util.Map;
38 42
@@ -129,16 +133,51 @@ public class EnigmaDumper extends StringStreamDumper {
129 @Override 133 @Override
130 public Dumper dumpClassDoc(JavaTypeInstance owner) { 134 public Dumper dumpClassDoc(JavaTypeInstance owner) {
131 if (mapper != null) { 135 if (mapper != null) {
136 List<String> recordComponentDocs = new LinkedList<>();
137
138 if (isRecord(owner)) {
139 ClassFile classFile = ((JavaRefTypeInstance) owner).getClassFile();
140 for (ClassFileField field : classFile.getFields()) {
141 if (field.getField().testAccessFlag(AccessFlag.ACC_STATIC)) {
142 continue;
143 }
144
145 EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getField().getDescriptor()));
146 if (mapping == null) {
147 continue;
148 }
149
150 String javaDoc = mapping.getJavadoc();
151 if (javaDoc != null) {
152 recordComponentDocs.add(String.format("@param %s %s", field.getFieldName(), javaDoc));
153 }
154 }
155 }
156
132 EntryMapping mapping = mapper.getDeobfMapping(getClassEntry(owner)); 157 EntryMapping mapping = mapper.getDeobfMapping(getClassEntry(owner));
158
159 String javadoc = null;
133 if (mapping != null) { 160 if (mapping != null) {
134 String javadoc = mapping.getJavadoc(); 161 javadoc = mapping.getJavadoc();
162 }
163
164 if (javadoc != null || !recordComponentDocs.isEmpty()) {
165 print("/**").newln();
135 if (javadoc != null) { 166 if (javadoc != null) {
136 print("/**").newln();
137 for (String line : javadoc.split("\\R")) { 167 for (String line : javadoc.split("\\R")) {
138 print(" * ").print(line).newln(); 168 print(" * ").print(line).newln();
139 } 169 }
140 print(" */").newln(); 170
171 if (!recordComponentDocs.isEmpty()) {
172 print(" * ").newln();
173 }
141 } 174 }
175
176 for (String componentDoc : recordComponentDocs) {
177 print(" * ").print(componentDoc).newln();
178 }
179
180 print(" */").newln();
142 } 181 }
143 } 182 }
144 return this; 183 return this;
@@ -186,7 +225,8 @@ public class EnigmaDumper extends StringStreamDumper {
186 225
187 @Override 226 @Override
188 public Dumper dumpFieldDoc(Field field, JavaTypeInstance owner) { 227 public Dumper dumpFieldDoc(Field field, JavaTypeInstance owner) {
189 if (mapper != null) { 228 boolean recordComponent = isRecord(owner) && !field.testAccessFlag(AccessFlag.ACC_STATIC);
229 if (mapper != null && !recordComponent) {
190 EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getDescriptor())); 230 EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getDescriptor()));
191 if (mapping != null) { 231 if (mapping != null) {
192 String javadoc = mapping.getJavadoc(); 232 String javadoc = mapping.getJavadoc();
@@ -336,4 +376,13 @@ public class EnigmaDumper extends StringStreamDumper {
336 return sb.toString(); 376 return sb.toString();
337 } 377 }
338 378
379 private boolean isRecord(JavaTypeInstance javaTypeInstance) {
380 if (javaTypeInstance instanceof JavaRefTypeInstance) {
381 ClassFile classFile = ((JavaRefTypeInstance) javaTypeInstance).getClassFile();
382 return classFile.getClassSignature().getSuperClass().getRawName().equals("java.lang.Record");
383 }
384
385 return false;
386 }
387
339} 388}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
index a183d2b8..ef904d7f 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
@@ -1,6 +1,8 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import java.util.Collection; 3import java.util.Collection;
4import java.util.List;
5import java.util.stream.Collectors;
4import java.util.stream.Stream; 6import java.util.stream.Stream;
5 7
6import javax.annotation.Nullable; 8import javax.annotation.Nullable;
@@ -13,7 +15,12 @@ import cuchaz.enigma.translation.Translator;
13import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; 15import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree;
14import cuchaz.enigma.translation.mapping.tree.EntryTree; 16import cuchaz.enigma.translation.mapping.tree.EntryTree;
15import cuchaz.enigma.translation.mapping.tree.HashEntryTree; 17import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
18import cuchaz.enigma.translation.representation.entry.ClassEntry;
16import cuchaz.enigma.translation.representation.entry.Entry; 19import cuchaz.enigma.translation.representation.entry.Entry;
20import cuchaz.enigma.translation.representation.entry.FieldEntry;
21
22import cuchaz.enigma.translation.representation.entry.MethodEntry;
23import cuchaz.enigma.utils.validation.Message;
17import cuchaz.enigma.utils.validation.ValidationContext; 24import cuchaz.enigma.utils.validation.ValidationContext;
18 25
19public class EntryRemapper { 26public class EntryRemapper {
@@ -21,6 +28,7 @@ public class EntryRemapper {
21 28
22 private final EntryResolver obfResolver; 29 private final EntryResolver obfResolver;
23 private final Translator deobfuscator; 30 private final Translator deobfuscator;
31 private final JarIndex jarIndex;
24 32
25 private final MappingValidator validator; 33 private final MappingValidator validator;
26 34
@@ -30,6 +38,7 @@ public class EntryRemapper {
30 this.obfResolver = jarIndex.getEntryResolver(); 38 this.obfResolver = jarIndex.getEntryResolver();
31 39
32 this.deobfuscator = new MappingTranslator(obfToDeobf, obfResolver); 40 this.deobfuscator = new MappingTranslator(obfToDeobf, obfResolver);
41 this.jarIndex = jarIndex;
33 42
34 this.validator = new MappingValidator(obfToDeobf, deobfuscator, jarIndex); 43 this.validator = new MappingValidator(obfToDeobf, deobfuscator, jarIndex);
35 } 44 }
@@ -51,6 +60,13 @@ public class EntryRemapper {
51 } 60 }
52 61
53 public <E extends Entry<?>> void mapFromObf(ValidationContext vc, E obfuscatedEntry, @Nullable EntryMapping deobfMapping, boolean renaming, boolean validateOnly) { 62 public <E extends Entry<?>> void mapFromObf(ValidationContext vc, E obfuscatedEntry, @Nullable EntryMapping deobfMapping, boolean renaming, boolean validateOnly) {
63 if (obfuscatedEntry instanceof FieldEntry) {
64 FieldEntry fieldEntry = (FieldEntry) obfuscatedEntry;
65 ClassEntry classEntry = fieldEntry.getParent();
66
67 mapRecordComponentGetter(vc, classEntry, fieldEntry, deobfMapping);
68 }
69
54 Collection<E> resolvedEntries = obfResolver.resolveEntry(obfuscatedEntry, renaming ? ResolutionStrategy.RESOLVE_ROOT : ResolutionStrategy.RESOLVE_CLOSEST); 70 Collection<E> resolvedEntries = obfResolver.resolveEntry(obfuscatedEntry, renaming ? ResolutionStrategy.RESOLVE_ROOT : ResolutionStrategy.RESOLVE_CLOSEST);
55 71
56 if (renaming && deobfMapping != null) { 72 if (renaming && deobfMapping != null) {
@@ -70,6 +86,35 @@ public class EntryRemapper {
70 mapFromObf(vc, obfuscatedEntry, null); 86 mapFromObf(vc, obfuscatedEntry, null);
71 } 87 }
72 88
89 // A little bit of a hack to also map the getter method for record fields/components.
90 private void mapRecordComponentGetter(ValidationContext vc, ClassEntry classEntry, FieldEntry fieldEntry, EntryMapping fieldMapping) {
91 if (!jarIndex.getEntryIndex().getClassAccess(classEntry).isRecord() || jarIndex.getEntryIndex().getFieldAccess(fieldEntry).isStatic()) {
92 return;
93 }
94
95 // Find all the methods in this record class
96 List<MethodEntry> classMethods = jarIndex.getEntryIndex().getMethods().stream()
97 .filter(entry -> classEntry.equals(entry.getParent()))
98 .collect(Collectors.toList());
99
100 MethodEntry methodEntry = null;
101
102 for (MethodEntry method : classMethods) {
103 // Find the matching record component getter via matching the names. My understanding is this is safe, failing this it may need to be a bit more intelligent
104 if (method.getName().equals(fieldEntry.getName()) && method.getDesc().toString().equals("()" + fieldEntry.getDesc())) {
105 methodEntry = method;
106 break;
107 }
108 }
109
110 if (methodEntry == null && fieldMapping != null) {
111 vc.raise(Message.UNKNOWN_RECORD_GETTER, fieldMapping.getTargetName());
112 return;
113 }
114
115 mapFromObf(vc, methodEntry, fieldMapping != null ? new EntryMapping(fieldMapping.getTargetName()) : null);
116 }
117
73 @Nullable 118 @Nullable
74 public EntryMapping getDeobfMapping(Entry<?> entry) { 119 public EntryMapping getDeobfMapping(Entry<?> entry) {
75 return obfToDeobf.get(entry); 120 return obfToDeobf.get(entry);
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java
index 113199a6..6bcdbde6 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java
@@ -15,6 +15,7 @@ public class Message {
15 public static final Message ILLEGAL_IDENTIFIER = create(Type.ERROR, "illegal_identifier"); 15 public static final Message ILLEGAL_IDENTIFIER = create(Type.ERROR, "illegal_identifier");
16 public static final Message RESERVED_IDENTIFIER = create(Type.ERROR, "reserved_identifier"); 16 public static final Message RESERVED_IDENTIFIER = create(Type.ERROR, "reserved_identifier");
17 public static final Message ILLEGAL_DOC_COMMENT_END = create(Type.ERROR, "illegal_doc_comment_end"); 17 public static final Message ILLEGAL_DOC_COMMENT_END = create(Type.ERROR, "illegal_doc_comment_end");
18 public static final Message UNKNOWN_RECORD_GETTER = create(Type.ERROR, "unknown_record_getter");
18 19
19 public static final Message STYLE_VIOLATION = create(Type.WARNING, "style_violation"); 20 public static final Message STYLE_VIOLATION = create(Type.WARNING, "style_violation");
20 21
diff --git a/enigma/src/main/resources/lang/en_us.json b/enigma/src/main/resources/lang/en_us.json
index 8862f22d..fd21faac 100644
--- a/enigma/src/main/resources/lang/en_us.json
+++ b/enigma/src/main/resources/lang/en_us.json
@@ -192,6 +192,7 @@
192 "validation.message.illegal_identifier.long": "Invalid character '%2$s' at position %3$d.", 192 "validation.message.illegal_identifier.long": "Invalid character '%2$s' at position %3$d.",
193 "validation.message.illegal_doc_comment_end": "Javadoc comment cannot contain the character sequence '*/'.", 193 "validation.message.illegal_doc_comment_end": "Javadoc comment cannot contain the character sequence '*/'.",
194 "validation.message.reserved_identifier": "'%s' is a reserved identifier.", 194 "validation.message.reserved_identifier": "'%s' is a reserved identifier.",
195 "validation.message.unknown_record_getter": "Could not find a matching record component getter for %s",
195 196
196 "crash.title": "%s - Crash Report", 197 "crash.title": "%s - Crash Report",
197 "crash.summary": "%s has crashed! =(", 198 "crash.summary": "%s has crashed! =(",