Skip to content

Commit 0eb8a9f

Browse files
committed
Port changes to Kotlin plugin
1 parent 36c4b3b commit 0eb8a9f

File tree

10 files changed

+211
-115
lines changed

10 files changed

+211
-115
lines changed

plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinWriter.java

Lines changed: 59 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,6 @@ public void writeClass(ClassNode node, TextBuffer buffer, int indent) {
274274
TextBuffer innerBuffer = new TextBuffer();
275275

276276
boolean hasContent = false;
277-
boolean enumFields = false;
278-
279-
List<StructRecordComponent> components = cl.getRecordComponents();
280277

281278
Set<StructField> fieldsToIgnore = new HashSet<>();
282279
Set<StructMethod> methodsToIgnore = new HashSet<>();
@@ -315,71 +312,37 @@ public void writeClass(ClassNode node, TextBuffer buffer, int indent) {
315312
methodsToIgnore.addAll(companionFunctions.keySet());
316313
}
317314

318-
// FIXME: fields don't have line mappings
319315
// fields
316+
List<StructRecordComponent> components = cl.getRecordComponents();
320317

321-
// Find the last field marked as an enum
322-
int maxEnumIdx = 0;
323-
for (int i = 0; i < cl.getFields().size(); i++) {
324-
StructField fd = cl.getFields().get(i);
325-
boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
326-
if (isEnum) {
327-
maxEnumIdx = i;
328-
}
329-
}
330-
331-
List<StructField> deferredEnumFields = new ArrayList<>();
318+
List<StructField> enumFields = new ArrayList<>();
319+
List<StructField> nonEnumFields = new ArrayList<>();
332320

333-
// Find any regular fields mixed in with the enum fields
334-
// This is invalid but allowed in bytecode.
335-
for (int i = 0; i < cl.getFields().size(); i++) {
336-
StructField fd = cl.getFields().get(i);
321+
for (StructField fd : cl.getFields()) {
337322
boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
338-
if (i < maxEnumIdx && !isEnum) {
339-
deferredEnumFields.add(fd);
323+
if (isEnum) {
324+
enumFields.add(fd);
325+
} else if (!fieldsToIgnore.contains(fd)) {
326+
nonEnumFields.add(fd);
340327
}
341328
}
342329

343-
for (StructField fd : cl.getFields()) {
344-
boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||
345-
wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())) ||
346-
deferredEnumFields.contains(fd) ||
347-
fieldsToIgnore.contains(fd);
348-
if (hide) continue;
349-
350-
if (components != null && fd.getAccessFlags() == (CodeConstants.ACC_FINAL | CodeConstants.ACC_PRIVATE) &&
351-
components.stream().anyMatch(c -> c.getName().equals(fd.getName()) && c.getDescriptor().equals(fd.getDescriptor()))) {
352-
// Record component field: skip it
353-
continue;
330+
boolean enums = false;
331+
for (StructField fd : enumFields) {
332+
if (enums) {
333+
innerBuffer.append(',').appendLineSeparator();
354334
}
335+
enums = true;
355336

356-
boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
357-
if (isEnum) {
358-
if (enumFields) {
359-
innerBuffer.append(',').appendLineSeparator();
360-
}
361-
enumFields = true;
362-
} else if (enumFields) {
363-
innerBuffer.append(';');
364-
innerBuffer.appendLineSeparator();
365-
innerBuffer.appendLineSeparator();
366-
enumFields = false;
367-
}
368-
369-
if (propertyData == null || enumFields) { // Enum fields are not considered Kotlin properties
370-
TextBuffer fieldBuffer = new TextBuffer();
371-
writeField(wrapper, cl, fd, fieldBuffer, indent + 1);
372-
fieldBuffer.clearUnassignedBytecodeMappingData();
373-
innerBuffer.append(fieldBuffer);
337+
writeField(innerBuffer, indent, fd, wrapper);
338+
hasContent = true;
339+
}
374340

375-
hasContent = true;
376-
}
341+
if (enums) {
342+
innerBuffer.append(';').appendLineSeparator();
377343
}
378344

379345
if (propertyData != null) {
380-
// Any deferred fields that are property fields should be removed to prevent duplication
381-
deferredEnumFields.removeAll(propertyData.associatedFields());
382-
383346
boolean addedLineAtEnd = false;
384347
for (KProperty prop : propertyData.properties()) {
385348
if (hasContent) {
@@ -394,7 +357,14 @@ public void writeClass(ClassNode node, TextBuffer buffer, int indent) {
394357
innerBuffer.appendLineSeparator();
395358
}
396359

397-
innerBuffer.append(propBuffer);
360+
StructField fd = prop.underlyingField();
361+
if (fd != null) {
362+
nonEnumFields.remove(fd);
363+
String initializer = fd.hasModifier(CodeConstants.ACC_STATIC) ? "<clinit> ()V" : "<init> ()V";
364+
innerBuffer.append(propBuffer, cl.qualifiedName, initializer);
365+
} else {
366+
innerBuffer.append(propBuffer);
367+
}
398368

399369
if (isMultiline) {
400370
innerBuffer.appendLineSeparator();
@@ -407,14 +377,25 @@ public void writeClass(ClassNode node, TextBuffer buffer, int indent) {
407377
if (!addedLineAtEnd && !propertyData.properties().isEmpty()) {
408378
innerBuffer.appendLineSeparator();
409379
}
410-
}
380+
} else {
381+
for (StructField fd : nonEnumFields) {
382+
boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||
383+
wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
384+
if (hide) continue;
411385

412-
// If any fields remaining were deferred but not enum fields, re-add them
413-
for (StructField fd2 : deferredEnumFields) {
414-
TextBuffer fieldBuffer = new TextBuffer();
415-
writeField(wrapper, cl, fd2, fieldBuffer, indent + 1);
416-
fieldBuffer.clearUnassignedBytecodeMappingData();
417-
innerBuffer.append(fieldBuffer);
386+
if (components != null && fd.getAccessFlags() == (CodeConstants.ACC_FINAL | CodeConstants.ACC_PRIVATE) &&
387+
components.stream().anyMatch(c -> c.getName().equals(fd.getName()) && c.getDescriptor().equals(fd.getDescriptor()))) {
388+
// Record component field: skip it
389+
continue;
390+
}
391+
392+
DecompilerContext.getLogger().writeMessage("Non-enum field " + fd.getName() + " is not associated with Kotlin property", IFernflowerLogger.Severity.WARN);
393+
394+
TextBuffer fieldBuffer = new TextBuffer();
395+
writeField(wrapper, wrapper.getClassStruct(), fd, fieldBuffer, indent + 1);
396+
String initializer = fd.hasModifier(CodeConstants.ACC_STATIC) ? "<clinit> ()V" : "<init> ()V";
397+
innerBuffer.append(fieldBuffer, wrapper.getClassStruct().qualifiedName, initializer);
398+
}
418399
}
419400

420401
// methods
@@ -526,7 +507,13 @@ private void writeKotlinFile(ClassNode node, TextBuffer buffer, int indent, KPro
526507
buffer.appendLineSeparator();
527508
}
528509

529-
buffer.append(propBuffer);
510+
StructField fd = prop.underlyingField();
511+
if (fd != null) {
512+
String initializer = fd.hasModifier(CodeConstants.ACC_STATIC) ? "<clinit> ()V" : "<init> ()V";
513+
buffer.append(propBuffer, cl.qualifiedName, initializer);
514+
} else {
515+
buffer.append(propBuffer);
516+
}
530517

531518
if (isMultiline) {
532519
buffer.appendLineSeparator();
@@ -543,11 +530,7 @@ private void writeKotlinFile(ClassNode node, TextBuffer buffer, int indent, KPro
543530

544531
for (StructField fd : cl.getFields()) {
545532
if (propertyData.associatedFields().contains(fd)) continue;
546-
547-
TextBuffer fieldBuffer = new TextBuffer();
548-
writeField(wrapper, cl, fd, fieldBuffer, indent);
549-
fieldBuffer.clearUnassignedBytecodeMappingData();
550-
buffer.append(fieldBuffer);
533+
writeField(buffer, indent, fd, wrapper);
551534
}
552535

553536
for (int i = 0; i < cl.getMethods().size(); i++) {
@@ -827,6 +810,13 @@ private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent,
827810
buffer.popNewlineGroup();
828811
}
829812

813+
private void writeField(TextBuffer buffer, int indent, StructField fd, ClassWrapper wrapper) {
814+
TextBuffer fieldBuffer = new TextBuffer();
815+
writeField(wrapper, wrapper.getClassStruct(), fd, fieldBuffer, indent + 1);
816+
String initializer = fd.hasModifier(CodeConstants.ACC_STATIC) ? "<clinit> ()V" : "<init> ()V";
817+
buffer.append(fieldBuffer, wrapper.getClassStruct().qualifiedName, initializer);
818+
}
819+
830820
public void writeField(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent) {
831821
boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE);
832822
boolean isDeprecated = fd.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED);

plugins/kotlin/src/main/java/org/vineflower/kotlin/struct/KProperty.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public TextBuffer stringify(int indent) {
9999

100100
if (initializer != null) {
101101
TextBuffer initializerBuf = initializer.toJava(indent);
102-
initializerBuf.clearUnassignedBytecodeMappingData();
102+
// initializerBuf.clearUnassignedBytecodeMappingData();
103103
if (flags.isDelegated) {
104104
buf.append(" by ")
105105
.append(initializerBuf);

plugins/kotlin/testData/results/pkg/TestClassicStringInterpolation.dec

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package pkg
22

33
public class TestClassicStringInterpolation {
4-
public final val x: Int = 5
4+
public final val x: Int = 5// 16
55

66
public fun stringInterpolation(x: Int, y: String) {
77
System.out.println("$x $y");// 5
@@ -25,6 +25,10 @@ public class TestClassicStringInterpolation {
2525
}
2626

2727
class 'pkg/TestClassicStringInterpolation' {
28+
method '<init> ()V' {
29+
5 3
30+
}
31+
2832
method 'stringInterpolation (ILjava/lang/String;)V' {
2933
d 6
3034
16 6
@@ -111,7 +115,10 @@ Lines mapping:
111115
10 <-> 12
112116
13 <-> 15
113117
14 <-> 16
118+
16 <-> 4
114119
18 <-> 19
115120
19 <-> 20
116121
22 <-> 23
117122
23 <-> 24
123+
Not mapped:
124+
3

plugins/kotlin/testData/results/pkg/TestConvertedK2JOps.dec

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package pkg
22

33
public class TestConvertedK2JOps {
4-
public final val list: List<String> = CollectionsKt.listOf(new java.lang.String[]{"a", "b", "c"})
5-
public final val set: Set<String> = SetsKt.setOf(new java.lang.String[]{"a", "b", "c"})
6-
public final val map: Map<String, String> = MapsKt.mapOf(new Pair[]{TuplesKt.to("a", "b"), TuplesKt.to("c", "d")})
4+
public final val list: List<String> = CollectionsKt.listOf(new java.lang.String[]{"a", "b", "c"})// 4
5+
public final val set: Set<String> = SetsKt.setOf(new java.lang.String[]{"a", "b", "c"})// 5
6+
public final val map: Map<String, String> = MapsKt.mapOf(new Pair[]{TuplesKt.to("a", "b"), TuplesKt.to("c", "d")})// 6
77
public final val any: Any = new Object()
88

99
public fun codeConstructs() {
@@ -12,6 +12,44 @@ public class TestConvertedK2JOps {
1212
}
1313

1414
class 'pkg/TestConvertedK2JOps' {
15+
method '<init> ()V' {
16+
c 3
17+
d 3
18+
11 3
19+
12 3
20+
16 3
21+
17 3
22+
1a 3
23+
1b 3
24+
1c 3
25+
28 4
26+
29 4
27+
2d 4
28+
2e 4
29+
32 4
30+
33 4
31+
36 4
32+
37 4
33+
38 4
34+
44 5
35+
45 5
36+
46 5
37+
47 5
38+
48 5
39+
49 5
40+
4a 5
41+
4e 5
42+
4f 5
43+
50 5
44+
51 5
45+
52 5
46+
53 5
47+
54 5
48+
57 5
49+
58 5
50+
59 5
51+
}
52+
1553
method 'codeConstructs ()V' {
1654
0 9
1755
1 9
@@ -26,7 +64,12 @@ class 'pkg/TestConvertedK2JOps' {
2664
}
2765

2866
Lines mapping:
67+
4 <-> 4
68+
5 <-> 5
69+
6 <-> 6
2970
10 <-> 10
3071
12 <-> 11
3172
Not mapped:
73+
3
74+
7
3275
11

0 commit comments

Comments
 (0)