Skip to content

Commit

Permalink
Update to match Vineflower 1.11.0
Browse files Browse the repository at this point in the history
  • Loading branch information
sschr15 committed Aug 31, 2024
1 parent f0d5e5f commit 07047ae
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 516 deletions.
2 changes: 1 addition & 1 deletion docgen/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
vineflowerVersion=1.9.2
vineflowerVersion=1.11.0-SNAPSHOT
100 changes: 16 additions & 84 deletions docgen/src/main/java/org/vineflower/docgen/UsageGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,101 +41,33 @@
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import io.pebbletemplates.pebble.utils.Pair;
import org.jetbrains.java.decompiler.api.DecompilerOption;
import org.jetbrains.java.decompiler.api.plugin.Plugin;
import org.jetbrains.java.decompiler.main.Init;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.slf4j.Logger;
import org.vineflower.docgen.util.Logging;

public class UsageGenerator {
private static final Logger LOGGER = Logging.logger();
private static final int PSF_MASK = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL;
private static final String ANNOTATION_PREFIX = "org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences$";
private static final boolean HAS_LEGACY_SHORT_NAME;

static {
boolean hasShortName = false;
try {
Class.forName(ANNOTATION_PREFIX + "ShortName");
hasShortName = true;
} catch (final ClassNotFoundException ignored) {
}
HAS_LEGACY_SHORT_NAME = hasShortName;
}

record Problem(String argumentName, String message) {}

record ArgumentInfo(String argumentName, String friendlyName, String legacyShortName, String description, String defaultValue, String type) {
static ArgumentInfo fromField(final Field field, final Consumer<Problem> problemConsumer) {
final String argumentName;
try {
argumentName = (String) field.get(null);
} catch (IllegalAccessException e) {
problemConsumer.accept(new Problem(field.getName(), "could not read field value"));
return null;
}

String friendlyName = annotationValueOrNull(field,"Name");
if (friendlyName == null) {
problemConsumer.accept(new Problem(argumentName, "missing @Name annotation"));
}
String description = annotationValueOrNull(field,"Description");
if (description == null) {
problemConsumer.accept(new Problem(argumentName, "missing @Description annotation"));
}
String defaultValue = valueOfOrNull(IFernflowerPreferences.getDefaults().get(argumentName));
// 1.10+
String legacyShortName = annotationValueOrNull(field, "ShortName");
String type = annotationValueOrNull(field, "Type");

return new ArgumentInfo(argumentName, friendlyName, legacyShortName, description, defaultValue, type);
}
}


public static boolean generate(final Path usageFile, final PebbleEngine engine) throws IOException {
final List<Problem> problems = new ArrayList<>();
final List<ArgumentInfo> info = Arrays.stream(IFernflowerPreferences.class.getFields())
.filter(UsageGenerator::isPublicStaticFinalString)
.filter(field -> field.isAnnotationPresent(IFernflowerPreferences.Name.class)) // basic sanity check
.map(f -> ArgumentInfo.fromField(f, problems::add))
.filter(Objects::nonNull)
.collect(Collectors.toList());

info.sort(Comparator.comparing(ArgumentInfo::argumentName));
Init.init();

if (!problems.isEmpty()) {
LOGGER.error("There were problems discovering information about argument usage:");
for (final var problem : problems) {
LOGGER.error("- '{}': {}", problem.argumentName(), problem.message());
}
return false;
}
List<Pair<String, List<DecompilerOption>>> options = DecompilerOption.getAllByPlugin()
.entrySet().stream()
.sorted(Comparator.comparing(entry -> entry.getKey() != null ? entry.getKey().id() : ""))
.map(entry -> {
String pluginName = entry.getKey() != null ? "Plugin: " + entry.getKey().id() : null;
List<DecompilerOption> pluginOptions = entry.getValue();
return new Pair<>(pluginName, pluginOptions);
})
.toList();

final PebbleTemplate tpl = engine.getTemplate("usage.peb");
try (final Writer writer = Files.newBufferedWriter(usageFile, StandardCharsets.UTF_8)) {
tpl.evaluate(writer, Map.of("args", info, "hasLegacyShortNames", HAS_LEGACY_SHORT_NAME));
tpl.evaluate(writer, Map.of("groups", options));
}
return true;
}

private static boolean isPublicStaticFinalString(final Field field) {
return String.class.equals(field.getType()) && (field.getModifiers() & PSF_MASK) == PSF_MASK;
}

private static String valueOfOrNull(final Object input) {
return input == null ? null : String.valueOf(input);
}

private static String annotationValueOrNull(final AnnotatedElement element, final String annotation) {
final String qualifiedName = ANNOTATION_PREFIX + annotation;
for (final Annotation a : element.getAnnotations()) {
if (a.annotationType().getName().equals(qualifiedName)) {
try {
return (String) a.getClass().getMethod("value").invoke(a);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException("Failed to read value field from annotation " + a.annotationType().getName() + " on " + element, e);
}
}
}
return null;
}
}
24 changes: 15 additions & 9 deletions docgen/src/main/resources/tmpl/usage.peb
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
{# @pebvariable name="args" type="java.util.List<org.vineflower.docgen.UsageGenerator.ArgumentInfo>" #}
{# @pebvariable name="hasLegacyShortNames" type="boolean" #}
{% for arg in args %}
```{option} {% if hasLegacyShortNames %}
--{{ arg.argumentName }}{% if arg.type is not null %}={{ "{" }}{{ arg.type }}{{ "}" }}{% endif %}, {%if arg.legacyShortName is not null %}-{{ arg.legacyShortName }}{% if arg.type is not null %}={{ "{" }}{{ arg.type }}{{ "}" }}{% endif %}{% endif %}
{# @pebvariable name="groups" type="java.util.List<io.pebbletemplates.pebble.utils.Pair<java.lang.String, java.util.List<org.jetbrains.java.decompiler.api.DecompilerOption>>>" #}
{% for group in groups %}

{% if group.left is not null %}
---
### {{ group.left }}
{% else %}
-{{ arg.argumentName }}={{ "{" }}value{{ "}" }}
### Base Decompiler Options
{% endif %}

{{ arg.description }}
{% for option in group.right %}
```{option} --{{ option.id }}={{ option.type }}

{%if arg.defaultValue %}
{# Escape $, MathJax uses it #}
{{ option.description | replace({'$': '\$'}) }}

**Default**: `{{ arg.defaultValue }}`
{% if option.defaultValue %}
**Default**: `{{ option.defaultValue }}`
{% endif %}

```

{% endfor %}

{% endfor %}
Loading

0 comments on commit 07047ae

Please sign in to comment.