From 34c3d99e4e1b26475c0909b592de53b590670507 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sun, 22 Oct 2023 14:16:05 +0200 Subject: [PATCH 1/3] Fix Fernflower crash caused by trying to serialize virtual class --- src/main/java/matcher/srcprocess/Fernflower.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/matcher/srcprocess/Fernflower.java b/src/main/java/matcher/srcprocess/Fernflower.java index fb39c473..d3d37f93 100644 --- a/src/main/java/matcher/srcprocess/Fernflower.java +++ b/src/main/java/matcher/srcprocess/Fernflower.java @@ -147,7 +147,7 @@ public StructClass getClass(String name) { if (ret != null) return ret; ClassInstance cls = env.getClsByName(name, nameType); - if (cls == null) return null; + if (cls == null || !cls.isReal()) return null; try { return addStructClass(cls, false); From b62d84c7a07d72037b2b1e87471616efe4c5ca57 Mon Sep 17 00:00:00 2001 From: NebelNidas <48808497+NebelNidas@users.noreply.github.com> Date: Wed, 6 Mar 2024 22:48:19 +0100 Subject: [PATCH 2/3] Update dependencies (#42) * Update dependencies * Add missing semicolon * Update to Mapping-IO 0.5 * Update dependencies, again --- build.gradle | 3 - gradle.properties | 16 +- src/main/java/matcher/gui/menu/FileMenu.java | 14 +- .../matcher/mapping/MappingWriterImpl.java | 346 ------------------ src/main/java/matcher/mapping/Mappings.java | 96 +++-- .../java/matcher/srcprocess/HtmlPrinter.java | 103 ++++-- 6 files changed, 128 insertions(+), 450 deletions(-) delete mode 100644 src/main/java/matcher/mapping/MappingWriterImpl.java diff --git a/build.gradle b/build.gradle index a9fe4b88..ad5b7452 100644 --- a/build.gradle +++ b/build.gradle @@ -86,9 +86,6 @@ dependencies { extraJavaModuleInfo { failOnMissingModuleInfo.set(false) // because of transitive dependencies - // Mapping-IO - automaticModule("net.fabricmc:mapping-io", "net.fabricmc.mappingio") - // CFR automaticModule("net.fabricmc:cfr", "cfr") diff --git a/gradle.properties b/gradle.properties index f3ccf2a6..54dfe34a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,19 +2,19 @@ org.gradle.jvmargs=-Xmx2G # Gradle Plugins -javafx_plugin_version = 0.0.13 +javafx_plugin_version = 0.1.0 shadow_version = 7.1.2 -extra_java_module_info_version = 1.3 +extra_java_module_info_version = 1.6 # Poject Properties version = 0.1.0 # Project Dependencies -asm_version = 9.4 -fabric_cfr_version = 0.2.0 +asm_version = 9.6 +fabric_cfr_version = 0.2.1 fabric_fernflower_version = 2.0.0 procyon_version = 0.6.0 -mappingio_version = 0.3.0 -javaparser_version = 3.24.2 -javafx_version = 17.0.2 -checkstyle_version = 10.3.4 +mappingio_version = 0.5.0 +javaparser_version = 3.25.6 +javafx_version = 21.0.1 +checkstyle_version = 10.12.5 diff --git a/src/main/java/matcher/gui/menu/FileMenu.java b/src/main/java/matcher/gui/menu/FileMenu.java index 59a7f9da..8a1f93dc 100644 --- a/src/main/java/matcher/gui/menu/FileMenu.java +++ b/src/main/java/matcher/gui/menu/FileMenu.java @@ -65,21 +65,17 @@ private void init() { getItems().add(menuItem); menuItem.setOnAction(event -> loadMappings(null)); - menuItem = new MenuItem("Load mappings (Enigma)"); + menuItem = new MenuItem("Load mappings (Enigma dir)"); getItems().add(menuItem); - menuItem.setOnAction(event -> loadMappings(MappingFormat.ENIGMA)); - - menuItem = new MenuItem("Load mappings (MCP dir)"); - getItems().add(menuItem); - menuItem.setOnAction(event -> loadMappings(MappingFormat.MCP)); + menuItem.setOnAction(event -> loadMappings(MappingFormat.ENIGMA_DIR)); menuItem = new MenuItem("Save mappings"); getItems().add(menuItem); menuItem.setOnAction(event -> saveMappings(null)); - menuItem = new MenuItem("Save mappings (Enigma)"); + menuItem = new MenuItem("Save mappings (Enigma dir)"); getItems().add(menuItem); - menuItem.setOnAction(event -> saveMappings(MappingFormat.ENIGMA)); + menuItem.setOnAction(event -> saveMappings(MappingFormat.ENIGMA_DIR)); menuItem = new MenuItem("Clear mappings"); getItems().add(menuItem); @@ -361,7 +357,7 @@ private static boolean isDirEmpty(Path dir) { } private static MappingFormat getFormat(Path file) { - if (Files.isDirectory(file)) return MappingFormat.ENIGMA; + if (Files.isDirectory(file)) return MappingFormat.ENIGMA_DIR; String name = file.getFileName().toString().toLowerCase(Locale.ENGLISH); diff --git a/src/main/java/matcher/mapping/MappingWriterImpl.java b/src/main/java/matcher/mapping/MappingWriterImpl.java deleted file mode 100644 index 55847100..00000000 --- a/src/main/java/matcher/mapping/MappingWriterImpl.java +++ /dev/null @@ -1,346 +0,0 @@ -package matcher.mapping; - -import java.io.Closeable; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.io.Writer; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; - -import net.fabricmc.mappingio.FlatMappingVisitor; -import net.fabricmc.mappingio.MappingFlag; -import net.fabricmc.mappingio.format.MappingFormat; - -public class MappingWriterImpl implements FlatMappingVisitor, Closeable { - public MappingWriterImpl(Path file, MappingFormat format) throws IOException { - this.file = file; - this.format = format; - - switch (format) { - case TINY: - case SRG: - writer = Files.newBufferedWriter(file, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE); - break; - default: - throw new IllegalArgumentException("invalid mapping format: "+format.name()); - } - } - - @Override - public Set getFlags() { - Set ret = EnumSet.of(MappingFlag.NEEDS_UNIQUENESS); - - ret.add(MappingFlag.NEEDS_SRC_METHOD_DESC); - - if (format == MappingFormat.SRG || format == MappingFormat.MCP) { - ret.add(MappingFlag.NEEDS_DST_METHOD_DESC); - } else { - ret.add(MappingFlag.NEEDS_SRC_FIELD_DESC); - } - - return ret; - } - - @Override - public void visitNamespaces(String srcNamespace, List dstNamespaces) { - try { - switch (format) { - case TINY: - writer.write("v1\t"); - writer.write(srcNamespace); - - for (String dstNamespace : dstNamespaces) { - writer.write('\t'); - writer.write(dstNamespace); - } - - writer.write('\n'); - break; - case MCP: - case SRG: - // not supported - break; - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - @Override - public void visitMetadata(String key, String value) { - try { - switch (format) { - case TINY: - switch (key) { - case Mappings.metaUidNextClass: - case Mappings.metaUidNextMethod: - case Mappings.metaUidNextField: - writer.write("# INTERMEDIARY-COUNTER "); - writer.write(key.equals(Mappings.metaUidNextClass) ? "class" : (key.equals(Mappings.metaUidNextMethod) ? "method" : "field")); - writer.write(' '); - writer.write(value); - writer.write('\n'); - break; - default: - // not supported - } - - break; - case MCP: - case SRG: - // not supported - break; - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - @Override - public boolean visitClass(String srcName, String[] dstNames) { - try { - switch (format) { - case TINY: - if (isAnyPresent(dstNames)) { - writer.write("CLASS\t"); - writer.write(srcName); - - for (String dstName : dstNames) { - writer.write('\t'); - - if (dstName != null) { - writer.write(dstName); - } - } - - writer.write('\n'); - } - - break; - case SRG: - if (isFirstPresent(dstNames)) { - writer.write("CL: "); - writer.write(srcName); - writer.write(' '); - writer.write(dstNames[0]); - writer.write('\n'); - } - - break; - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - - return true; - } - - @Override - public void visitClassComment(String srcName, String[] dstNames, String comment) { - switch (format) { - case TINY: - case SRG: - // not supported - break; - } - } - - @Override - public boolean visitMethod(String srcClsName, String srcName, String srcDesc, String[] dstClsNames, String[] dstNames, String[] dstDescs) { - try { - switch (format) { - case TINY: - if (isAnyPresent(dstNames)) { - writer.write("METHOD\t"); - writer.write(srcClsName); - writer.write('\t'); - writer.write(srcDesc); - writer.write('\t'); - writer.write(srcName); - - for (String dstName : dstNames) { - writer.write('\t'); - if (dstName != null) writer.write(dstName); - } - - writer.write('\n'); - } - - break; - case SRG: - if (isFirstPresent(dstNames)) { - writer.write("MD: "); - writer.write(srcClsName); - writer.write('/'); - writer.write(srcName); - writer.write(' '); - writer.write(srcDesc); - writer.write(' '); - writer.write(isFirstPresent(dstClsNames) ? dstClsNames[0] : srcClsName); // TODO: handle null better - writer.write('/'); - writer.write(dstNames[0]); - writer.write(' '); - writer.write(dstDescs[0]); // TODO: handle null - writer.write('\n'); - } - - break; - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - - return true; - } - - @Override - public void visitMethodComment(String srcClsName, String srcName, String srcDesc, - String[] dstClsNames, String[] dstNames, String[] dstDescs, - String comment) { - switch (format) { - case TINY: - case SRG: - // not supported - break; - } - } - - @Override - public boolean visitMethodArg(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName, - String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames) { - switch (format) { - case TINY: - case SRG: - // not supported - return false; - } - - return true; - } - - @Override - public void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc, int argIndex, int lvIndex, String srcArgName, - String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames, - String comment) { - switch (format) { - case TINY: - case SRG: - // not supported - break; - } - } - - @Override - public boolean visitMethodVar(String srcClsName, String srcMethodName, String srcMethodDesc, - int asmIndex, int lvIndex, int startOpIdx, String srcVarName, - String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstVarNames) { - switch (format) { - case TINY: - case SRG: - // not supported - return false; - } - - return true; - } - - @Override - public void visitMethodVarComment(String srcClsName, String srcMethodName, String srcMethodDesc, - int asmIndex, int lvIndex, int startOpIdx, String srcVarName, - String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstVarNames, - String comment) { - switch (format) { - case TINY: - case SRG: - // not supported - break; - } - } - - @Override - public boolean visitField(String srcClsName, String srcName, String srcDesc, String[] dstClsNames, String[] dstNames, String[] dstDescs) { - try { - switch (format) { - case TINY: - if (isAnyPresent(dstNames)) { - writer.write("FIELD\t"); - writer.write(srcClsName); - writer.write('\t'); - writer.write(srcDesc); - writer.write('\t'); - writer.write(srcName); - - for (String dstName : dstNames) { - writer.write('\t'); - if (dstName != null) writer.write(dstName); - } - - writer.write('\n'); - } - - break; - case SRG: - if (isFirstPresent(dstNames)) { - writer.write("FD: "); - writer.write(srcClsName); - writer.write('/'); - writer.write(srcName); - writer.write(' '); - writer.write(isFirstPresent(dstClsNames) ? dstClsNames[0] : srcClsName); - writer.write('/'); - writer.write(dstNames[0]); - writer.write('\n'); - } - - break; - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - - return true; - } - - @Override - public void visitFieldComment(String srcClsName, String srcName, String srcDesc, - String[] dstClsNames, String[] dstNames, String[] dstDescs, - String comment) { - switch (format) { - case TINY: - case SRG: - // not supported - break; - } - } - - public void flush() throws IOException { - if (writer != null) writer.flush(); - } - - @Override - public void close() throws IOException { - if (writer != null) writer.close(); - } - - private static boolean isAnyPresent(String[] strs) { - if (strs == null) return false; - - for (String s : strs) { - if (s != null) return true; - } - - return false; - } - - private static boolean isFirstPresent(String[] strs) { - return strs != null && strs.length > 0 && strs[0] != null; - } - - private final Path file; - private final MappingFormat format; - private final Writer writer; -} diff --git a/src/main/java/matcher/mapping/Mappings.java b/src/main/java/matcher/mapping/Mappings.java index 97827a78..0194a08f 100644 --- a/src/main/java/matcher/mapping/Mappings.java +++ b/src/main/java/matcher/mapping/Mappings.java @@ -1,8 +1,6 @@ package matcher.mapping; -import java.io.Closeable; import java.io.IOException; -import java.io.UncheckedIOException; import java.net.URI; import java.nio.file.Path; import java.util.ArrayList; @@ -124,7 +122,7 @@ public boolean visitMethodArg(int argPosition, int lvIndex, String srcArgName) { } @Override - public boolean visitMethodVar(int asmIndex, int lvIndex, int startOpIdx, String srcArgName) { + public boolean visitMethodVar(int asmIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcArgName) { arg = null; cur = var = getMethodVar(-1, lvIndex, startOpIdx, asmIndex, false); @@ -511,67 +509,61 @@ public static boolean save(Path file, MappingFormat format, LocalClassEnv env, List vars = new ArrayList<>(); Set> exportedHierarchies = verbosity == MappingsExportVerbosity.MINIMAL ? Util.newIdentityHashSet() : null; - try (Closeable closable = format == MappingFormat.TINY_2 || format == MappingFormat.ENIGMA ? MappingWriter.create(file, format) : new MappingWriterImpl(file, format)) { - FlatMappingVisitor writer = closable instanceof FlatMappingVisitor ? (FlatMappingVisitor) closable : new RegularAsFlatMappingVisitor((MappingVisitor) closable); + FlatMappingVisitor writer = new RegularAsFlatMappingVisitor(MappingWriter.create(file, format)); + writer.visitNamespaces(nsNames.get(0), nsNames.subList(1, nsNames.size())); - writer.visitNamespaces(nsNames.get(0), nsNames.subList(1, nsNames.size())); - - for (ClassInstance cls : classes) { - String srcClsName = cls.getName(nsTypes.get(0)); - if (srcClsName == null) continue; - boolean hasAnyDstName = false; - - for (int i = 1; i < nsTypes.size(); i++) { - NameType dstType = nsTypes.get(i); - String dstName = cls.getName(dstType); // FIXME: this may actually not be unique, record exported and suffix conflicting? - - if (dstName != null && (dstName.equals(srcClsName) || dstType != dstType.withMapped(false) && cls.hasNoFullyMappedName())) { - // don't save no-op or partial mappings (partial = only outer class is mapped) - dstName = null; - } + for (ClassInstance cls : classes) { + String srcClsName = cls.getName(nsTypes.get(0)); + if (srcClsName == null) continue; + boolean hasAnyDstName = false; - hasAnyDstName |= dstName != null; - dstClassNames[i - 1] = dstName; - } + for (int i = 1; i < nsTypes.size(); i++) { + NameType dstType = nsTypes.get(i); + String dstName = cls.getName(dstType); // FIXME: this may actually not be unique, record exported and suffix conflicting? - if (!hasAnyDstName - && (!format.supportsComments || cls.getMappedComment() == null) - && !shouldExportAny(cls.getMethods(), format, nsTypes, verbosity, forAnyInput, exportedHierarchies) - && !shouldExportAny(cls.getFields(), format, nsTypes)) { - continue; // no data for the class, skip + if (dstName != null && (dstName.equals(srcClsName) || dstType != dstType.withMapped(false) && cls.hasNoFullyMappedName())) { + // don't save no-op or partial mappings (partial = only outer class is mapped) + dstName = null; } - if (!writer.visitClass(srcClsName, dstClassNames)) { - continue; - } + hasAnyDstName |= dstName != null; + dstClassNames[i - 1] = dstName; + } - // comment + if (!hasAnyDstName + && (!format.supportsComments || cls.getMappedComment() == null) + && !shouldExportAny(cls.getMethods(), format, nsTypes, verbosity, forAnyInput, exportedHierarchies) + && !shouldExportAny(cls.getFields(), format, nsTypes)) { + continue; // no data for the class, skip + } - if (cls.getMappedComment() != null) writer.visitClassComment(srcClsName, dstClassNames, cls.getMappedComment()); + if (!writer.visitClass(srcClsName, dstClassNames)) { + continue; + } - if (fieldsFirst) { - exportFields(cls, srcClsName, dstClassNames, format, nsTypes, - dstMemberNames, dstMemberDescs, fields, memberCmp, writer); - } + // comment - exportMethods(cls, srcClsName, dstClassNames, - format, nsTypes, verbosity, forAnyInput, - dstMemberNames, dstMemberDescs, dstVarNames, - methods, vars, exportedHierarchies, - memberCmp, methodArgCmp, methodVarCmp, - writer); + if (cls.getMappedComment() != null) writer.visitClassComment(srcClsName, dstClassNames, cls.getMappedComment()); - if (!fieldsFirst) { - exportFields(cls, srcClsName, dstClassNames, format, nsTypes, - dstMemberNames, dstMemberDescs, fields, memberCmp, writer); - } + if (fieldsFirst) { + exportFields(cls, srcClsName, dstClassNames, format, nsTypes, + dstMemberNames, dstMemberDescs, fields, memberCmp, writer); } - writer.visitEnd(); - } catch (UncheckedIOException e) { - throw e.getCause(); + exportMethods(cls, srcClsName, dstClassNames, + format, nsTypes, verbosity, forAnyInput, + dstMemberNames, dstMemberDescs, dstVarNames, + methods, vars, exportedHierarchies, + memberCmp, methodArgCmp, methodVarCmp, + writer); + + if (!fieldsFirst) { + exportFields(cls, srcClsName, dstClassNames, format, nsTypes, + dstMemberNames, dstMemberDescs, fields, memberCmp, writer); + } } + writer.visitEnd(); return true; } @@ -672,7 +664,7 @@ private static void exportMethods(ClassInstance cls, String srcClsName, String[] dstClassNames, dstMethodNames, dstMemberDescs, dstVarNames); } else { writer.visitMethodVar(srcClsName, srcName, desc, - var.getAsmIndex(), var.getLvIndex(), var.getStartOpIdx(), srcVarName, + var.getAsmIndex(), var.getLvIndex(), var.getStartOpIdx(), -1, srcVarName, dstClassNames, dstMethodNames, dstMemberDescs, dstVarNames); } } @@ -687,7 +679,7 @@ private static void exportMethods(ClassInstance cls, String srcClsName, String[] comment); } else { writer.visitMethodVarComment(srcClsName, srcName, desc, - var.getAsmIndex(), var.getLvIndex(), var.getStartOpIdx(), srcVarName, + var.getAsmIndex(), var.getLvIndex(), var.getStartOpIdx(), -1, srcVarName, dstClassNames, dstMethodNames, dstMemberDescs, dstVarNames, comment); } diff --git a/src/main/java/matcher/srcprocess/HtmlPrinter.java b/src/main/java/matcher/srcprocess/HtmlPrinter.java index 4598f4ce..1bca18ca 100644 --- a/src/main/java/matcher/srcprocess/HtmlPrinter.java +++ b/src/main/java/matcher/srcprocess/HtmlPrinter.java @@ -1,5 +1,5 @@ /* - * Most of this file is copied from DefaultPrettyPrinterVisitor, + * Most of this file is copied from DefaultPrettyPrinterVisitor (commit 19e0559), * tweaked to output HTML instead of plain text. Original license: * * Copyright (C) 2011, 2013-2021 The JavaParser Team. @@ -27,13 +27,12 @@ import static com.github.javaparser.utils.Utils.isNullOrEmpty; import java.util.ArrayList; -import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; +import java.util.regex.Pattern; import com.github.javaparser.ast.ArrayCreationLevel; import com.github.javaparser.ast.ImportDeclaration; @@ -216,7 +215,7 @@ protected void printTypeArgs(final NodeWithTypeArguments nodeWithTypeArgument NodeList typeArguments = nodeWithTypeArguments.getTypeArguments().orElse(null); if (!isNullOrEmpty(typeArguments)) { - printer.print("<"); + printer.print("<"); // < for (final Iterator i = typeArguments.iterator(); i.hasNext(); ) { final Type t = i.next(); @@ -227,14 +226,14 @@ protected void printTypeArgs(final NodeWithTypeArguments nodeWithTypeArgument } } - printer.print(">"); + printer.print(">"); // > } } @Override protected void printTypeParameters(final NodeList args, final Void arg) { if (!isNullOrEmpty(args)) { - printer.print("<"); + printer.print("<"); // < for (final Iterator i = args.iterator(); i.hasNext(); ) { final TypeParameter t = i.next(); @@ -245,7 +244,7 @@ protected void printTypeParameters(final NodeList args, final Voi } } - printer.print(">"); + printer.print(">"); // > } } @@ -318,6 +317,19 @@ public void visit(final ClassOrInterfaceDeclaration n, final Void arg) { } } + if (!n.getPermittedTypes().isEmpty()) { + printer.print(" permits "); + + for (final Iterator i = n.getPermittedTypes().iterator(); i.hasNext(); ) { + final ClassOrInterfaceType c = i.next(); + c.accept(this, arg); + + if (i.hasNext()) { + printer.print(", "); + } + } + } + printer.println(" {"); printer.indent(); @@ -344,6 +356,8 @@ public void visit(RecordDeclaration n, Void arg) { n.getName().accept(this, arg); printer.print(""); + printTypeParameters(n.getTypeParameters(), arg); + printer.print("("); if (!isNullOrEmpty(n.getParameters())) { @@ -358,7 +372,6 @@ public void visit(RecordDeclaration n, Void arg) { } printer.print(")"); - printTypeParameters(n.getTypeParameters(), arg); if (!n.getImplementedTypes().isEmpty()) { printer.print(" implements "); @@ -391,12 +404,11 @@ public void visit(final JavadocComment n, final Void arg) { printOrphanCommentsBeforeThisChildNode(n); if (getOption(ConfigOption.PRINT_COMMENTS).isPresent() && getOption(ConfigOption.PRINT_JAVADOC).isPresent()) { - printer.println("/**"); + printer.print(""); + printer.println(n.getHeader()); final String commentContent = Utils.normalizeEolInTextBlock(HtmlUtil.escape(n.getContent()), getOption(ConfigOption.END_OF_LINE_CHARACTER).get().asString()); String[] lines = commentContent.split("\\R"); - boolean skippingLeadingEmptyLines = true; - boolean prependEmptyLine = false; - boolean prependSpace = Arrays.stream(lines).anyMatch(line -> !line.isEmpty() && !line.startsWith(" ")); + List strippedLines = new ArrayList<>(); for (String line : lines) { final String trimmedLine = line.trim(); @@ -406,7 +418,14 @@ public void visit(final JavadocComment n, final Void arg) { } line = Utils.trimTrailingSpaces(line); + strippedLines.add(line); + } + boolean skippingLeadingEmptyLines = true; + boolean prependEmptyLine = false; + boolean prependSpace = strippedLines.stream().anyMatch(line -> !line.isEmpty() && !line.startsWith(" ")); + + for (String line : strippedLines) { if (line.isEmpty()) { if (!skippingLeadingEmptyLines) { prependEmptyLine = true; @@ -429,7 +448,9 @@ public void visit(final JavadocComment n, final Void arg) { } } - printer.println(" */"); + printer.print(" ") + .print(n.getFooter()) + .println(""); } } @@ -678,11 +699,11 @@ public void visit(final InstanceOfExpr n, final Void arg) { printComment(n.getComment(), arg); n.getExpression().accept(this, arg); printer.print(" instanceof "); - n.getType().accept(this, arg); - if (n.getName().isPresent()) { - printer.print(" "); - n.getName().get().accept(this, arg); + if (n.getPattern().isPresent()) { + n.getPattern().get().accept(this, arg); + } else { + n.getType().accept(this, arg); } } @@ -983,6 +1004,13 @@ public void visit(final ConstructorDeclaration n, final Void arg) { printer.print(""); printer.print("("); + n.getReceiverParameter().ifPresent(rp -> { + rp.accept(this, arg); + + if (!isNullOrEmpty(n.getParameters())) { + printer.print(", "); + } + }); if (!n.getParameters().isEmpty()) { for (final Iterator i = n.getParameters().iterator(); i.hasNext(); ) { @@ -1024,7 +1052,6 @@ public void visit(final CompactConstructorDeclaration n, final Void arg) { printComment(n.getComment(), arg); printMemberAnnotations(n.getAnnotations(), arg); printModifiers(n.getModifiers()); - printTypeParameters(n.getTypeParameters(), arg); if (n.isGeneric()) { @@ -1065,7 +1092,6 @@ public void visit(final MethodDeclaration n, final Void arg) { } printOrphanCommentsBeforeThisChildNode(n); - printComment(n.getComment(), arg); printMemberAnnotations(n.getAnnotations(), arg); printModifiers(n.getModifiers()); @@ -1209,13 +1235,13 @@ private void printSwitchNode(SwitchNode n, Void arg) { printer.println(") {"); if (n.getEntries() != null) { - if (getOption(ConfigOption.INDENT_CASE_IN_SWITCH).isPresent()) printer.indent(); + indentIf(getOption(ConfigOption.INDENT_CASE_IN_SWITCH).isPresent()); for (final SwitchEntry e : n.getEntries()) { e.accept(this, arg); } - if (getOption(ConfigOption.INDENT_CASE_IN_SWITCH).isPresent()) printer.indent(); + unindentIf(getOption(ConfigOption.INDENT_CASE_IN_SWITCH).isPresent()); } printer.print("}"); @@ -1748,8 +1774,9 @@ public void visit(final LineComment n, final Void arg) { } printer.print(""); - printer.print("// ") - .println(Utils.normalizeEolInTextBlock(HtmlUtil.escape(n.getContent()), "").trim()); + printer.print(n.getHeader()) + .print(" ") + .println(Utils.normalizeEolInTextBlock(HtmlUtil.escape(RTRIM.matcher(n.getContent()).replaceAll("")), "")); printer.println(""); } @@ -1761,15 +1788,17 @@ public void visit(final BlockComment n, final Void arg) { final String commentContent = Utils.normalizeEolInTextBlock(n.getContent(), getOption(ConfigOption.END_OF_LINE_CHARACTER).get().asString()); String[] lines = commentContent.split("\\R", -1); // as BlockComment should not be formatted, -1 to preserve any trailing empty line if present - printer.print("/*"); + printer.print(""); + printer.print(n.getHeader()); for (int i = 0; i < (lines.length - 1); i++) { printer.print(lines[i]); printer.print(getOption(ConfigOption.END_OF_LINE_CHARACTER).get().asString()); // Avoids introducing indentation in blockcomments. ie: do not use println() as it would trigger indentation at the next print call. } - printer.print(lines[lines.length - 1]); // last line is not followed by a newline, and simply terminated with `*/` - printer.println("*/"); + printer.print(lines[lines.length - 1]) // last line is not followed by a newline, and simply terminated with `*/` + .print(n.getFooter()) + .println(""); } @Override @@ -1893,8 +1922,7 @@ private void printOrphanCommentsBeforeThisChildNode(final Node node) { private void printOrphanCommentsEnding(final Node node) { if (!getOption(ConfigOption.PRINT_COMMENTS).isPresent()) return; - // extract all nodes for which the position/range is indicated to avoid to skip orphan comments - List everything = node.getChildNodes().stream().filter(n -> n.getRange().isPresent()).collect(Collectors.toList()); + List everything = new ArrayList<>(node.getChildNodes()); sortByBeginPosition(everything); if (everything.isEmpty()) { @@ -1918,6 +1946,20 @@ private void printOrphanCommentsEnding(final Node node) { } } + private void indentIf(boolean expr) { + if (expr) printer.indent(); + } + + private void unindentIf(boolean expr) { + if (expr) printer.unindent(); + } + + private Optional getOption(ConfigOption option) { + return configuration.get(new DefaultConfigurationOption(option)); + } + + // Matcher-introduced methods + private static boolean canAddNewLine(Node n) { Node prev = getPrev(n); @@ -1955,10 +1997,7 @@ private static Node getNext(Node n) { return parent.getChildNodes().get(idx + 1); } - private Optional getOption(ConfigOption option) { - return configuration.get(new DefaultConfigurationOption(option)); - } - + private static Pattern RTRIM = Pattern.compile("\\s+$"); protected final TypeResolver typeResolver; protected boolean instantiationAhead; protected int recursionCounter; From b8dc2d21d53f9eb02515f330648c006615cdfda2 Mon Sep 17 00:00:00 2001 From: NebelNidas <48808497+NebelNidas@users.noreply.github.com> Date: Wed, 6 Mar 2024 23:34:23 +0100 Subject: [PATCH 3/3] Use Vineflower instead of Fernflower (#39) --- build.gradle | 6 +- gradle.properties | 2 +- .../matcher/srcprocess/BuiltinDecompiler.java | 2 +- .../java/matcher/srcprocess/Fernflower.java | 322 ------------------ .../java/matcher/srcprocess/Vineflower.java | 145 ++++++++ src/main/java/module-info.java | 2 +- 6 files changed, 151 insertions(+), 328 deletions(-) delete mode 100644 src/main/java/matcher/srcprocess/Fernflower.java create mode 100644 src/main/java/matcher/srcprocess/Vineflower.java diff --git a/build.gradle b/build.gradle index ad5b7452..e74c8882 100644 --- a/build.gradle +++ b/build.gradle @@ -62,7 +62,7 @@ dependencies { implementation "org.ow2.asm:asm-util:${asm_version}" implementation "com.github.javaparser:javaparser-core:${javaparser_version}" implementation "net.fabricmc:cfr:${fabric_cfr_version}" - implementation "net.fabricmc:fabric-fernflower:${fabric_fernflower_version}" + implementation "org.vineflower:vineflower:${vineflower_version}" implementation "org.bitbucket.mstrobel:procyon-compilertools:${procyon_version}" // JavaFX for all platforms (needed for cross-platform fat jar) @@ -89,8 +89,8 @@ extraJavaModuleInfo { // CFR automaticModule("net.fabricmc:cfr", "cfr") - // Fernflower - automaticModule("net.fabricmc:fabric-fernflower", "intellij.fernflower") + // Vineflower + automaticModule("org.vineflower:vineflower", "org.vineflower.vineflower") // Procyon automaticModule("org.bitbucket.mstrobel:procyon-compilertools", "procyon.compilertools") diff --git a/gradle.properties b/gradle.properties index 54dfe34a..2c775d7f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ version = 0.1.0 # Project Dependencies asm_version = 9.6 fabric_cfr_version = 0.2.1 -fabric_fernflower_version = 2.0.0 +vineflower_version = 1.9.3 procyon_version = 0.6.0 mappingio_version = 0.5.0 javaparser_version = 3.25.6 diff --git a/src/main/java/matcher/srcprocess/BuiltinDecompiler.java b/src/main/java/matcher/srcprocess/BuiltinDecompiler.java index 28b7d237..5ccffd91 100644 --- a/src/main/java/matcher/srcprocess/BuiltinDecompiler.java +++ b/src/main/java/matcher/srcprocess/BuiltinDecompiler.java @@ -4,7 +4,7 @@ public enum BuiltinDecompiler { CFR("CFR", Cfr::new), - FERNFLOWER("Fernflower", Fernflower::new), + VINEFLOWER("Vineflower", Vineflower::new), PROCYON("Procyon", Procyon::new); BuiltinDecompiler(String name, Supplier supplier) { diff --git a/src/main/java/matcher/srcprocess/Fernflower.java b/src/main/java/matcher/srcprocess/Fernflower.java deleted file mode 100644 index d3d37f93..00000000 --- a/src/main/java/matcher/srcprocess/Fernflower.java +++ /dev/null @@ -1,322 +0,0 @@ -package matcher.srcprocess; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.lang.reflect.Field; -import java.util.AbstractMap; -import java.util.ArrayDeque; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.jar.Manifest; - -import org.jetbrains.java.decompiler.main.ClassesProcessor; -import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; -import org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger; -import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; -import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; -import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; -import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer; -import org.jetbrains.java.decompiler.main.extern.IResultSaver; -import org.jetbrains.java.decompiler.modules.renamer.ConverterHelper; -import org.jetbrains.java.decompiler.modules.renamer.IdentifierConverter; -import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; -import org.jetbrains.java.decompiler.struct.ContextUnit; -import org.jetbrains.java.decompiler.struct.IDecompiledData; -import org.jetbrains.java.decompiler.struct.StructClass; -import org.jetbrains.java.decompiler.struct.StructContext; -import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; -import org.jetbrains.java.decompiler.util.DataInputFullStream; -import org.jetbrains.java.decompiler.util.TextBuffer; - -import matcher.NameType; -import matcher.type.ClassEnv; -import matcher.type.ClassFeatureExtractor; -import matcher.type.ClassInstance; - -public class Fernflower implements Decompiler { - @Override - public String decompile(ClassInstance cls, ClassFeatureExtractor env, NameType nameType) { - // invoke ff with on-demand class lookup into matcher's state and string based output - Map properties = new HashMap<>(IFernflowerPreferences.DEFAULTS); - properties.put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "1"); - - ResultSaver resultSaver = new ResultSaver(); - DecompiledData data = new DecompiledData(); - BytecodeProvider bcProvider = new BytecodeProvider(env, nameType); - MatcherStructContext structContext = new MatcherStructContext(resultSaver, data, new LazyLoader(bcProvider), env, nameType, bcProvider); - ClassesProcessor classProcessor = new ClassesProcessor(structContext); - PoolInterceptor interceptor = null; - IIdentifierRenamer renamer; - IdentifierConverter converter; - - if ("1".equals(properties.get(IFernflowerPreferences.RENAME_ENTITIES))) { - renamer = new ConverterHelper(); - interceptor = new PoolInterceptor(); - converter = new IdentifierConverter(structContext, renamer, interceptor); - } else { - renamer = null; - converter = null; - } - - data.classProcessor = classProcessor; - data.converter = converter; - - IFernflowerLogger logger = new PrintStreamLogger(System.out); - DecompilerContext context = new DecompilerContext(properties, logger, structContext, classProcessor, interceptor); - DecompilerContext.setCurrentContext(context); - - try { - structContext.addSpace(cls, true); - - // queue inner classes as well - if (!cls.getInnerClasses().isEmpty()) { - Queue toAdd = new ArrayDeque<>(cls.getInnerClasses()); - ClassInstance innerCls; - - while ((innerCls = toAdd.poll()) != null) { - structContext.addSpace(innerCls, true); - toAdd.addAll(innerCls.getInnerClasses()); - } - } - - if (converter != null) converter.rename(); - - classProcessor.loadClasses(renamer); - structContext.saveContext(); - } finally { - DecompilerContext.setCurrentContext(null); - } - - String ret = resultSaver.results.get(cls.getName(nameType)); - - if (ret != null) { - return ret; - } else { - throw new RuntimeException("decompiling "+cls+" didn't yield the expected result (available: "+resultSaver.results.keySet()+")"); - } - } - - private static class MatcherStructContext extends StructContext { - @SuppressWarnings("unchecked") - MatcherStructContext(IResultSaver saver, IDecompiledData decompiledData, LazyLoader loader, ClassEnv env, NameType nameType, BytecodeProvider bcProvider) { - super(saver, decompiledData, loader); - - this.loader = loader; - this.env = env; - this.nameType = nameType; - this.bcProvider = bcProvider; - - try { - Field f = StructContext.class.getDeclaredField("units"); - f.setAccessible(true); - units = (Map) f.get(this); - - f = StructContext.class.getDeclaredField("classes"); - f.setAccessible(true); - classes = (Map) f.get(this); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - - ownedUnit = units.get(""); - unownedUnit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, unownedUnitFilename, false, saver, decompiledData); // avoids producing superfluous output - units.put(unownedUnitFilename, unownedUnit); - } - - public void addSpace(ClassInstance cls, boolean isOwn) { - try { - addStructClass(cls, isOwn); - } catch (IOException ex) { - String message = "Corrupted class: " + cls.getName(nameType); - DecompilerContext.getLogger().writeMessage(message, ex); - } - } - - @Override - public StructClass getClass(String name) { - if (DEBUG) System.out.printf("getClass(%s)%n", name); - - // use classes as a cache, load anything missing on demand - StructClass ret = classes.get(name); - if (ret != null) return ret; - - ClassInstance cls = env.getClsByName(name, nameType); - if (cls == null || !cls.isReal()) return null; - - try { - return addStructClass(cls, false); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private StructClass addStructClass(ClassInstance cls, boolean isOwn) throws IOException { - String name = cls.getName(nameType); - byte[] data = bcProvider.get(name); // BytecodeProvider has a name->byte[] cache to avoid redundant cls.serialize invocations - if (data == null) throw new IllegalStateException(); - DataInputFullStream in = new DataInputFullStream(data); - - StructClass cl = StructClass.create(in, isOwn, loader); - classes.put(cl.qualifiedName, cl); - - ContextUnit unit = isOwn ? ownedUnit : unownedUnit; - unit.addClass(cl, name.substring(name.lastIndexOf('/') + 1)+".class"); - loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(pathPrefix+name+pathSuffix, null)); - - return cl; - } - - @Override - public Map getClasses() { - return emulatedClasses; - } - - protected final LazyLoader loader; - protected final ClassEnv env; - protected final NameType nameType; - protected final BytecodeProvider bcProvider; - protected final Map units; - protected final Map classes; - protected final ContextUnit ownedUnit; - protected final ContextUnit unownedUnit; - - private final Map emulatedClasses = new AbstractMap() { - @Override - public boolean containsKey(Object key) { - return get(key) != null; - } - - @Override - public StructClass get(Object key) { - if (!(key instanceof String)) return null; - - return MatcherStructContext.this.getClass((String) key); - } - - @Override - public int size() { - return classes.size(); - } - - @Override - public Set> entrySet() { - Set> snapshot = new HashSet<>(classes.entrySet()); // copy to hide concurrent modifications from on-demand additions - - return snapshot; - } - }; - } - - private static class ResultSaver implements IResultSaver { - @Override - public void saveFolder(String path) { } - @Override - public void copyFile(String source, String path, String entryName) { } - @Override - public void createArchive(String path, String archiveName, Manifest manifest) { } - @Override - public void saveDirEntry(String path, String archiveName, String entryName) { } - @Override - public void copyEntry(String source, String path, String archiveName, String entry) { } - @Override - public void closeArchive(String path, String archiveName) { } - @Override - public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) { } - - @Override - public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) { - if (DEBUG) System.out.printf("saveClassFile(%s, %s, %s, %s, %s)%n", path, qualifiedName, entryName, content, Arrays.toString(mapping)); - - results.put(qualifiedName, content); - } - - Map results = new HashMap<>(); - } - - private static class DecompiledData implements IDecompiledData { - @Override - public String getClassEntryName(StructClass cl, String entryname) { - if (DEBUG) System.out.printf("getClassEntryName(%s, %s)%n", cl, entryname); - - ClassNode node = classProcessor.getMapRootClasses().get(cl.qualifiedName); - - if (node.type != ClassNode.CLASS_ROOT) { - return null; - } else if (converter != null) { - String simpleClassName = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/') + 1); - return entryname.substring(0, entryname.lastIndexOf('/') + 1) + simpleClassName + ".java"; - } else { - return entryname.substring(0, entryname.lastIndexOf(".class")) + ".java"; - } - } - - @Override - public String getClassContent(StructClass cl) { - if (DEBUG) System.out.printf("getClassContent(%s)%n", cl); - - try { - TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE); - buffer.append(DecompilerContext.getProperty(IFernflowerPreferences.BANNER).toString()); - classProcessor.writeClass(cl, buffer); - return buffer.toString(); - } catch (Throwable t) { - DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", t); - return null; - } - } - - protected ClassesProcessor classProcessor; - protected IdentifierConverter converter; - } - - private static class BytecodeProvider implements IBytecodeProvider { - BytecodeProvider(ClassEnv env, NameType nameType) { - this.env = env; - this.nameType = nameType; - } - - @Override - public byte[] getBytecode(String externalPath, String internalPath) throws IOException { - if (DEBUG) System.out.printf("getBytecode(%s, %s)%n", externalPath, internalPath); - - if (externalPath.startsWith(pathPrefix) && externalPath.endsWith(pathSuffix)) { - String name = externalPath.substring(pathPrefix.length(), externalPath.length() - pathSuffix.length()); - byte[] ret = get(name); - if (ret != null) return ret; - } - - throw new FileNotFoundException("can't find class for "+externalPath); - } - - public byte[] get(String name) { - byte[] ret = cache.get(name); - if (ret != null) return ret; - - ClassInstance cls = env.getClsByName(name, nameType); - - if (cls != null) { - ret = cls.serialize(nameType); - cache.put(name, ret); - } - - return ret; - } - - protected final ClassEnv env; - protected final NameType nameType; - - private final Map cache = new HashMap<>(); - } - - private static final boolean DEBUG = false; - - private static final String pathPrefix = "/matchenv/"; - private static final String pathSuffix = ".class"; - private static final String unownedUnitFilename = "foreign"; -} diff --git a/src/main/java/matcher/srcprocess/Vineflower.java b/src/main/java/matcher/srcprocess/Vineflower.java new file mode 100644 index 00000000..81ffe027 --- /dev/null +++ b/src/main/java/matcher/srcprocess/Vineflower.java @@ -0,0 +1,145 @@ +package matcher.srcprocess; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.jar.Manifest; + +import org.jetbrains.java.decompiler.main.decompiler.BaseDecompiler; +import org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger; +import org.jetbrains.java.decompiler.main.extern.IContextSource; +import org.jetbrains.java.decompiler.main.extern.IContextSource.IOutputSink; +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.main.extern.IResultSaver; + +import matcher.NameType; +import matcher.type.ClassFeatureExtractor; +import matcher.type.ClassInstance; + +public class Vineflower implements Decompiler { + @Override + public String decompile(ClassInstance cls, ClassFeatureExtractor env, NameType nameType) { + // invoke VF with on-demand class lookup into matcher's state and string based output + Map properties = new HashMap<>(IFernflowerPreferences.DEFAULTS); + properties.put(IFernflowerPreferences.REMOVE_BRIDGE, "0"); + properties.put(IFernflowerPreferences.REMOVE_SYNTHETIC, "0"); + properties.put(IFernflowerPreferences.INDENT_STRING, "\n"); + properties.put(IFernflowerPreferences.THREADS, String.valueOf(Math.max(1, Runtime.getRuntime().availableProcessors() - 2))); + properties.put(IFernflowerPreferences.LOG_LEVEL, IFernflowerLogger.Severity.WARN.name()); + + OutputSink sink = new OutputSink(); + BaseDecompiler decompiler = new BaseDecompiler(NopResultSaver.INSTANCE, properties, new PrintStreamLogger(System.out)); + decompiler.addSource(new MatcherClsSource(cls, nameType, sink)); + decompiler.decompileContext(); + return sink.results.get(cls.getName(nameType)); + } + + private static class NopResultSaver implements IResultSaver { + @Override + public void saveFolder(String path) { } + @Override + public void copyFile(String source, String path, String entryName) { } + @Override + public void createArchive(String path, String archiveName, Manifest manifest) { } + @Override + public void saveDirEntry(String path, String archiveName, String entryName) { } + @Override + public void copyEntry(String source, String path, String archiveName, String entry) { } + @Override + public void closeArchive(String path, String archiveName) { } + @Override + public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) { } + @Override + public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) { } + + static final NopResultSaver INSTANCE = new NopResultSaver(); + } + + private static class MatcherClsSource implements IContextSource { + MatcherClsSource(ClassInstance cls, NameType nameType, IOutputSink outputSink) { + this.cls = cls; + this.nameType = nameType; + this.outputSink = outputSink; + } + + @Override + public String getName() { + return "Matcher class provider"; + } + + private String getClsName(ClassInstance cls, NameType nameType) { + String name = cls.getName(nameType); + return name == null || name.isEmpty() ? cls.getName() : name; + } + + @Override + public Entries getEntries() { + String name = getClsName(cls, nameType); + List entries = new ArrayList<>(); + entries.add(Entry.parse(name)); + bytecodeByClsName.put(name, cls.serialize(nameType)); + + for (ClassInstance innerCls : cls.getInnerClasses()) { + String innerName = getClsName(innerCls, nameType); + entries.add(Entry.parse(innerName)); + bytecodeByClsName.put(innerName, innerCls.serialize(nameType)); + } + + return new Entries(entries, List.of(), List.of()); + } + + @Override + public InputStream getInputStream(String resource) throws IOException { + resource = resource.substring(0, resource.length() - ".class".length()); + byte[] bytecode; + + if ((bytecode = bytecodeByClsName.get(resource)) == null) { + throw new IOException("Requested class not in decompilation scope: "+resource); + } + + return new ByteArrayInputStream(bytecode); + } + + @Override + public IOutputSink createOutputSink(IResultSaver saver) { + return outputSink; + } + + private final ClassInstance cls; + private final NameType nameType; + private final IOutputSink outputSink; + /** name->byte[] cache to avoid redundant cls.serialize invocations. */ + private final Map bytecodeByClsName = new HashMap<>(); + } + + private static class OutputSink implements IOutputSink { + @Override + public void begin() { } + + @Override + public void acceptClass(String qualifiedName, String fileName, String content, int[] mapping) { + if (DEBUG) System.out.printf("acceptClass(%s, %s, %s, %s)%n", qualifiedName, fileName, content, Arrays.toString(mapping)); + + results.put(qualifiedName, content); + } + + @Override + public void acceptDirectory(String directory) { } + + @Override + public void acceptOther(String path) { } + + @Override + public void close() throws IOException { } + + private Map results = new HashMap<>(); + } + + private static final boolean DEBUG = false; +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 861c1551..d6a200d7 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -13,7 +13,7 @@ requires cfr; requires com.github.javaparser.core; - requires intellij.fernflower; + requires org.vineflower.vineflower; requires java.prefs; requires transitive javafx.base; requires transitive javafx.controls;