chore(config-compiler-plugin): Remove deprecated gradle plugin. Use the new javac plugin instead!
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/jfmod Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2023-07-09 13:44:19 +02:00
parent e4d510f164
commit e7bfc67bf5
Signed by: Johannes
GPG Key ID: E76429612C2929F4
25 changed files with 0 additions and 1334 deletions

View File

@ -1,49 +0,0 @@
import io.gitlab.jfronny.scripts.*
import javax.lang.model.element.Modifier.*
plugins {
id("java-gradle-plugin")
id("jf.maven-publish")
id("jf.codegen")
}
group = rootProject.group
version = rootProject.version
repositories {
mavenCentral()
maven("https://maven.frohnmeyer-wds.de/artifacts")
maven("https://maven.fabricmc.net/")
}
dependencies {
compileOnly(gradleApi())
implementation("org.apache.ant:ant:${prop("ant_version")}")
implementation("io.gitlab.jfronny:commons-gson:${prop("commons_version")}")
implementation("org.ow2.asm:asm:${prop("asm_version")}")
implementation("org.ow2.asm:asm-commons:${prop("asm_version")}")
implementation(project(":libjf-config-core-v1")) { isTransitive = false }
}
gradlePlugin {
plugins {
create("simplePlugin") {
id = "io.gitlab.jfronny.libjf.libjf-config-compiler-plugin"
implementationClass = "io.gitlab.jfronny.libjf.config.plugin.ConfigPlugin"
}
}
}
tasks.publish.get().dependsOn(tasks.build.get())
rootProject.tasks.deployDebug.dependsOn(tasks.publish.get())
sourceSets {
main {
generate(project) {
`class`("io.gitlab.jfronny.libjf.config.plugin", "BuildMetadata") {
modifiers(PUBLIC)
field("IS_RELEASE", project.hasProperty("release"), PUBLIC, STATIC, FINAL)
}
}
}
}

View File

@ -1,54 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin;
import io.gitlab.jfronny.libjf.config.plugin.util.ZipCompressor;
import org.apache.tools.zip.ZipOutputStream;
import org.gradle.api.GradleException;
import org.gradle.api.internal.file.copy.CopyAction;
import org.gradle.api.internal.file.copy.CopyActionProcessingStream;
import org.gradle.api.tasks.WorkResult;
import org.gradle.api.tasks.WorkResults;
import org.objectweb.asm.Type;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
public class ConfigInjectCopyAction implements CopyAction {
private final File zipFile;
private final ZipCompressor compressor;
private final String encoding;
private final boolean preserveFileTimestamps;
private final String modId;
private final Set<Type> knownConfigClasses = new HashSet<>();
public ConfigInjectCopyAction(File zipFile,
ZipCompressor compressor,
String encoding,
boolean preserveFileTimestamps,
String modId) {
this.zipFile = zipFile;
this.compressor = compressor;
this.encoding = encoding;
this.preserveFileTimestamps = preserveFileTimestamps;
this.modId = modId;
}
@Override
public WorkResult execute(CopyActionProcessingStream stream) {
try (ZipOutputStream zipOutStr = compressor.createArchiveOutputStream(zipFile)) {
if (encoding != null) {
zipOutStr.setEncoding(encoding);
}
AtomicBoolean fmjFound = new AtomicBoolean(false);
stream.process(details -> fmjFound.compareAndSet(false,
new StreamAction(zipOutStr, zipFile, preserveFileTimestamps, modId, knownConfigClasses)
.processFile(details)
));
} catch (IOException e) {
throw new GradleException("Could not create ZIP " + zipFile, e);
}
return WorkResults.didWork(true);
}
}

View File

@ -1,38 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin;
import io.gitlab.jfronny.libjf.config.plugin.util.GradleVersionUtil;
import io.gitlab.jfronny.libjf.config.plugin.util.ZipCompressor;
import org.gradle.api.file.DuplicatesStrategy;
import org.gradle.api.internal.file.copy.CopyAction;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.bundling.Jar;
public abstract class ConfigInjectTask extends Jar {
@Input
public abstract Property<String> getModId();
private final GradleVersionUtil versionUtil;
public ConfigInjectTask() {
super();
setDuplicatesStrategy(DuplicatesStrategy.FAIL);
versionUtil = new GradleVersionUtil(getProject().getGradle().getGradleVersion());
}
@Override
protected CopyAction createCopyAction() {
return new ConfigInjectCopyAction(
getArchiveFile().get().getAsFile(),
getInternalCompressor(),
this.getMetadataCharset(),
isPreserveFileTimestamps(),
getModId().get()
);
}
@Internal
protected ZipCompressor getInternalCompressor() {
return versionUtil.getInternalCompressor(getEntryCompression(), this);
}
}

View File

@ -1,20 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.BasePluginExtension;
import org.gradle.api.tasks.bundling.Jar;
import java.io.File;
public class ConfigPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getTasks().register("injectCompiledConfig", ConfigInjectTask.class, task -> {
task.from(project.getTasks().named("jar", Jar.class));
task.getModId().set(project.getExtensions().getByType(BasePluginExtension.class).getArchivesName());
task.getArchiveClassifier().set("config-inject");
task.getDestinationDirectory().set(new File(project.getBuildDir(), "devlibs"));
});
}
}

View File

@ -1,195 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.libjf.config.plugin.asm.ConfigInjectClassTransformer;
import io.gitlab.jfronny.libjf.config.plugin.asm.NotAConfigClassException;
import io.gitlab.jfronny.libjf.config.plugin.fmj.FabricModJsonTransformer;
import io.gitlab.jfronny.libjf.config.plugin.util.*;
import org.apache.tools.zip.*;
import org.gradle.api.GradleException;
import org.gradle.api.file.FileCopyDetails;
import org.objectweb.asm.*;
import java.io.*;
import java.util.GregorianCalendar;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
public class StreamAction {
private static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = (new GregorianCalendar(1980, 1, 1, 0, 0, 0)).getTimeInMillis();
private final ZipOutputStream zipOutStr;
private final File zipFile;
private final boolean preserveFileTimestamps;
private final String modId;
private final Set<Type> knownConfigClasses;
public StreamAction(ZipOutputStream zipOutStr, File zipFile, boolean preserveFileTimestamps, String modId, Set<Type> knownConfigClasses) {
this.zipOutStr = zipOutStr;
this.zipFile = zipFile;
this.preserveFileTimestamps = preserveFileTimestamps;
this.modId = modId;
this.knownConfigClasses = knownConfigClasses;
}
public boolean processFile(FileCopyDetails details) {
try {
if (details.isDirectory()) {
visitDirectory(details);
return false;
} else {
return visitFile(details);
}
} catch (Throwable e) {
throw new GradleException("Could not add " + details + " to ZIP " + zipFile, e);
}
}
private void visitDirectory(FileCopyDetails details) throws IOException {
String path = details.getRelativePath().getPathString() + "/";
ZipEntry archiveEntry = new ZipEntry(path);
archiveEntry.setTime(getArchiveTimeFor(details.getLastModified()));
archiveEntry.setUnixMode(UnixStat.DIR_FLAG | details.getMode());
zipOutStr.putNextEntry(archiveEntry);
zipOutStr.closeEntry();
}
private boolean visitFile(FileCopyDetails details) throws IOException {
if (details.getPath().endsWith(".jar")) {
return processArchive(details);
} else if (details.getPath().endsWith(".class")) {
processClass(details);
} else if (details.getPath().equals("fabric.mod.json")) {
processFMJ(details);
return true;
} else {
ZipEntry archiveEntry = new ZipEntry(details.getRelativePath().getPathString());
archiveEntry.setTime(getArchiveTimeFor(details.getLastModified()));
archiveEntry.setUnixMode(UnixStat.FILE_FLAG | details.getMode());
zipOutStr.putNextEntry(archiveEntry);
details.copyTo(zipOutStr);
zipOutStr.closeEntry();
}
return false;
}
private boolean processArchive(FileCopyDetails details) throws IOException {
try (ZipFile archive = new ZipFile(details.getFile())) {
AtomicBoolean fmjFound = new AtomicBoolean(false);
EnumerationSpliterator.stream(archive.getEntries())
.map(RelativeArchivePath::new)
.map(ArchiveFileTreeElement::new)
.filter(it -> it.getRelativePath().isFile())
.forEach(archiveElement -> {
fmjFound.compareAndSet(false, visitArchiveFile(archiveElement, archive));
});
return fmjFound.get();
}
}
private void visitArchiveDirectory(RelativeArchivePath archiveDir) throws IOException {
zipOutStr.putNextEntry(archiveDir.entry);
zipOutStr.closeEntry();
}
private boolean visitArchiveFile(ArchiveFileTreeElement archiveFile, ZipFile archive) {
RelativeArchivePath archiveFilePath = archiveFile.getRelativePath();
try {
if (archiveFile.isClassFile()) {
processClass(archiveFilePath, archive);
} else if (archiveFilePath.getPathString().equals("fabric.mod.json")) {
processFMJ(archiveFilePath, archive);
return true;
} else {
copyArchiveEntry(archiveFilePath, archive);
}
return false;
} catch (Throwable e) {
throw new GradleException("Could not read archive entry " + archiveFilePath.getPathString(), e);
}
}
private void copyArchiveEntry(RelativeArchivePath archiveFile, ZipFile archive) throws IOException {
ZipEntry entry = new ZipEntry(archiveFile.entry.getName());
entry.setTime(getArchiveTimeFor(archiveFile.entry.getTime()));
RelativeArchivePath path = new RelativeArchivePath(entry);
addParentDirectories(path);
zipOutStr.putNextEntry(path.entry);
try (InputStream is = archive.getInputStream(archiveFile.entry)) {
byte[] buffer = new byte[1024];
int n;
while (-1 != (n = is.read(buffer))) {
zipOutStr.write(buffer, 0, n);
}
}
zipOutStr.closeEntry();
}
private void addParentDirectories(RelativeArchivePath file) throws IOException {
if (file != null) {
addParentDirectories(file.getParent());
if (!file.isFile()) {
visitArchiveDirectory(file);
}
}
}
private void processClass(RelativeArchivePath file, ZipFile archive) throws IOException {
ZipEntry zipEntry = new ZipEntry(file.getPathString());
addParentDirectories(new RelativeArchivePath(zipEntry));
processClass(archive.getInputStream(file.entry).readAllBytes(), file.getPathString(), file.entry.getTime());
}
private void processClass(FileCopyDetails details) throws IOException {
try (InputStream is = new FileInputStream(details.getFile())) {
processClass(is.readAllBytes(), details.getPath(), details.getLastModified());
}
}
private void processClass(byte[] klazz, String path, long lastModified) throws IOException {
try {
final ClassReader reader = new ClassReader(klazz);
final ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
final ConfigInjectClassTransformer transformer = new ConfigInjectClassTransformer(modId, writer, knownConfigClasses);
reader.accept(transformer, ClassReader.EXPAND_FRAMES);
klazz = writer.toByteArray();
System.out.println("Injected config registration into " + path);
} catch (NotAConfigClassException notAConfigClass) {
// Use original bytes
}
ZipEntry archiveEntry = new ZipEntry(path);
archiveEntry.setTime(getArchiveTimeFor(lastModified));
zipOutStr.putNextEntry(archiveEntry);
zipOutStr.write(klazz);
zipOutStr.closeEntry();
}
private void processFMJ(RelativeArchivePath file, ZipFile archive) throws IOException {
ZipEntry zipEntry = new ZipEntry(file.getPathString());
addParentDirectories(new RelativeArchivePath(zipEntry));
processFMJ(archive.getInputStream(file.entry), file.getPathString(), file.entry.getTime());
}
private void processFMJ(FileCopyDetails details) throws IOException {
try (InputStream is = new FileInputStream(details.getFile())) {
processFMJ(is, details.getPath(), details.getLastModified());
}
}
private void processFMJ(InputStream fmj, String path, long lastModified) throws IOException {
ZipEntry archiveEntry = new ZipEntry(path);
archiveEntry.setTime(getArchiveTimeFor(lastModified));
zipOutStr.putNextEntry(archiveEntry);
// Leave this closeable open, as everything else will break the writer
try (JsonReader reader = new JsonReader(new InputStreamReader(fmj));
JsonWriter writer = new JsonWriter(new OutputStreamWriter(new DelegatingUncloseableOutputStream(zipOutStr)))) {
FabricModJsonTransformer.transform(reader, writer, knownConfigClasses);
}
zipOutStr.closeEntry();
}
private long getArchiveTimeFor(long timestamp) {
return preserveFileTimestamps ? timestamp : CONSTANT_TIME_FOR_ZIP_ENTRIES;
}
}

View File

@ -1,40 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.asm;
import org.gradle.api.GradleException;
import org.objectweb.asm.AnnotationVisitor;
import java.util.List;
import static org.objectweb.asm.Opcodes.*;
public class AnnotationMetaGatheringVisitor extends AnnotationVisitor {
private final List<String> referencedConfigs;
public AnnotationMetaGatheringVisitor(AnnotationVisitor annotationVisitor, List<String> referencedConfigs) {
super(ASM9, annotationVisitor);
this.referencedConfigs = referencedConfigs;
}
@Override
public AnnotationVisitor visitArray(String name) {
return switch (name) {
case "referencedConfigs" -> new ArrayVisitor<>(super.visitArray(name), referencedConfigs);
default -> throw new GradleException("Unknown field in category or JfConfig annotation: " + name);
};
}
private static class ArrayVisitor<T> extends AnnotationVisitor {
private final List<T> target;
protected ArrayVisitor(AnnotationVisitor annotationVisitor, List<T> target) {
super(ASM9, annotationVisitor);
this.target = target;
}
@Override
public void visit(String name, Object value) {
super.visit(name, value);
target.add((T) value);
}
}
}

View File

@ -1,228 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.asm;
import io.gitlab.jfronny.libjf.config.api.v1.Category;
import io.gitlab.jfronny.libjf.config.api.v1.JfConfig;
import io.gitlab.jfronny.libjf.config.plugin.BuildMetadata;
import io.gitlab.jfronny.libjf.config.plugin.asm.value.DiscoveredValue;
import io.gitlab.jfronny.libjf.config.plugin.util.ClInitInjectVisitor;
import io.gitlab.jfronny.libjf.config.plugin.util.GeneratorAdapter2;
import org.gradle.api.GradleException;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import java.util.*;
import static io.gitlab.jfronny.libjf.config.plugin.asm.value.KnownTypes.*;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Type.*;
public class ConfigInjectClassTransformer extends ClassVisitor {
// Field names
public static final String PREFIX = "libjf$config$";
public static final String INIT_METHOD = PREFIX + "clinit";
public static final String REGISTER_METHOD = PREFIX + "register";
public static final String BUILDER_ROOT = PREFIX + "root";
public static final String CLINIT = "<clinit>";
// Transformation metadata
public final String modId;
public Type current;
private final Set<Type> knownConfigClasses;
// Transformer state
private final List<String> categories = new LinkedList<>();
private final List<String> referencedConfigs = new LinkedList<>();
private final List<String> presets = new LinkedList<>();
private final List<String> verifiers = new LinkedList<>();
private final List<DiscoveredValue> values = new LinkedList<>();
private boolean initFound = false;
private TransformerMode mode;
public boolean isCategory() {
return mode == TransformerMode.CONFIG_CATEGORY || isConfig();
}
public void ensureCategory() {
if (!isCategory()) throw new NotAConfigClassException();
}
public boolean isConfig() {
return mode == TransformerMode.CONFIG_ROOT;
}
public boolean isOther() {
return mode == TransformerMode.OTHER;
}
public ConfigInjectClassTransformer(String modId, ClassVisitor cw, Set<Type> knownConfigClasses) {
super(ASM9, cw);
this.modId = modId;
this.knownConfigClasses = knownConfigClasses;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
this.current = Type.getType('L' + name + ';');
mode = TransformerMode.OTHER;
}
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (mode == null) throw new IllegalStateException("Attempted to visit annotation before class");
if (descriptor.equals(Type.getDescriptor(Category.class)) && isOther()) {
mode = TransformerMode.CONFIG_CATEGORY;
return new AnnotationMetaGatheringVisitor(super.visitAnnotation(descriptor, visible), referencedConfigs);
}
else if (descriptor.equals(Type.getDescriptor(JfConfig.class)) && isOther()) {
mode = TransformerMode.CONFIG_ROOT;
knownConfigClasses.add(current);
return new AnnotationMetaGatheringVisitor(super.visitAnnotation(descriptor, visible), referencedConfigs);
} else {
return super.visitAnnotation(descriptor, visible);
}
}
@Override
public void visitNestMember(String nestMember) {
ensureCategory();
categories.add(nestMember);
super.visitNestMember(nestMember);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
ensureCategory();
if (isConfig()) {
if (CLINIT.equals(name)) {
initFound = true;
return new ClInitInjectVisitor(
super.visitMethod(access, name, descriptor, signature, exceptions),
current.getInternalName(),
INIT_METHOD,
"()V"
);
}
}
if (name.startsWith(PREFIX)) {
throw new GradleException("This class declares methods generated by this plugin manually. Do not transform classes twice!");
}
if ((access & ACC_STATIC) == ACC_STATIC) {
// Possibly add verifier or preset
return new MethodMetaGatheringVisitor(
super.visitMethod(access, name, descriptor, signature, exceptions),
name, presets, verifiers);
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
ensureCategory();
if ((access & ACC_STATIC) == ACC_STATIC) {
return new FieldMetaGatheringVisitor(super.visitField(access, name, descriptor, signature, value), name, values, Type.getType(descriptor));
}
return super.visitField(access, name, descriptor, signature, value);
}
@Override
public void visitEnd() {
ensureCategory();
if (isConfig()) {
if (!initFound) {
// Generate <clinit> if missing
GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, CLINIT, "()V", null, null);
m.invokeStatic(current, new Method(INIT_METHOD, "()V"));
m.returnValue();
m.endMethod();
}
// Generate main method
{
GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, INIT_METHOD, "()V", null, null);
m.comment("Migrate files from old paths");
m.invokeIStatic(CONFIG_HOLDER_TYPE, new Method("getInstance", CONFIG_HOLDER_TYPE, new Type[0]));
m.push(modId);
m.invokeInterface(CONFIG_HOLDER_TYPE, new Method("migrateFiles", "(Ljava/lang/String;)V"));
m.comment("Invoke DSL, continue in " + BUILDER_ROOT);
m.push(modId);
m.invokeIStatic(DSL_TYPE, new Method("create", DSL_DEFAULTED_TYPE, new Type[]{STRING_TYPE}));
m.λ("apply",
getMethodDescriptor(CONFIG_BUILDER_FUNCTION_TYPE),
getMethodType(CONFIG_BUILDER_TYPE, CONFIG_BUILDER_TYPE),
current.getInternalName(),
BUILDER_ROOT);
m.invokeInterface(DSL_DEFAULTED_TYPE, new Method("config", CONFIG_INSTANCE_TYPE, new Type[]{CONFIG_BUILDER_FUNCTION_TYPE}));
m.pop();
m.returnValue();
m.endMethod();
}
// Generate register method for impl
// This method does nothing but is needed to properly implement the interface
// This also ensures the static initializer is called
{
GeneratorAdapter2 m = method(ACC_PUBLIC | ACC_STATIC, REGISTER_METHOD, getMethodDescriptor(VOID_TYPE, DSL_DEFAULTED_TYPE), null, null);
m.comment("This placeholder method is referenced in the FMJ to ensure the static initializer is called");
m.returnValue();
m.endMethod();
}
}
{
Type builderType = isConfig() ? CONFIG_BUILDER_TYPE : CATEGORY_BUILDER_TYPE;
GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, BUILDER_ROOT, getMethodDescriptor(builderType, builderType), null, null);
m.loadArg(0);
for (String name : referencedConfigs) {
m.comment("Reference the config of \"" + name + "\"");
m.push(name);
dslInvoke(m, "referenceConfig", CATEGORY_BUILDER_TYPE, STRING_TYPE);
}
for (String name : categories) {
m.comment("Register the category defined in " + name);
m.push(camelCase(name.substring(current.getInternalName().length())));
m.λ("apply",
getMethodDescriptor(CATEGORY_BUILDER_FUNCTION_TYPE),
getMethodType(CATEGORY_BUILDER_TYPE, CATEGORY_BUILDER_TYPE),
name,
BUILDER_ROOT);
dslInvoke(m, "category", CATEGORY_BUILDER_TYPE, STRING_TYPE, CATEGORY_BUILDER_FUNCTION_TYPE);
}
for (String verifier : verifiers) {
m.comment("Register the verifier \"" + verifier + "\"");
m.runnable(current.getInternalName(), verifier);
dslInvoke(m, "addVerifier", CATEGORY_BUILDER_TYPE, RUNNABLE_TYPE);
}
for (String preset : presets) {
m.comment("Register the preset \"" + preset + "\"");
m.push(preset);
m.runnable(current.getInternalName(), preset);
dslInvoke(m, "addPreset", CATEGORY_BUILDER_TYPE, STRING_TYPE, RUNNABLE_TYPE);
}
for (DiscoveredValue value : values) {
m.comment("Register the value \"" + value + "\"");
value.generateRegistration(m, this);
}
m.returnValue();
m.endMethod();
for (DiscoveredValue value : values) {
value.generateλ(this);
}
}
super.visitEnd();
}
public void dslInvoke(GeneratorAdapter m, String name, Type returnType, Type... arguments) {
Type builderType = isConfig() ? CONFIG_BUILDER_TYPE : CATEGORY_BUILDER_TYPE;
m.invokeInterface(builderType, new Method(name, returnType, arguments));
if (isConfig()) m.checkCast(CONFIG_BUILDER_TYPE);
}
private String camelCase(String s) {
return Character.toLowerCase(s.charAt(0)) + s.substring(1);
}
public GeneratorAdapter2 method(int access, String name, String descriptor, String signature, String[] exceptions) {
if (BuildMetadata.IS_RELEASE && !name.equals(CLINIT)) access |= ACC_SYNTHETIC;
return new GeneratorAdapter2(super.visitMethod(access, name, descriptor, signature, exceptions), access, name, descriptor);
}
}

View File

@ -1,59 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.asm;
import io.gitlab.jfronny.libjf.config.api.v1.Entry;
import io.gitlab.jfronny.libjf.config.plugin.asm.value.DiscoveredValue;
import org.objectweb.asm.*;
import java.util.List;
import static org.objectweb.asm.Opcodes.*;
public class FieldMetaGatheringVisitor extends FieldVisitor {
private final String name;
private final List<DiscoveredValue> values;
private final Type type;
protected FieldMetaGatheringVisitor(FieldVisitor fieldVisitor, String name, List<DiscoveredValue> values, Type type) {
super(ASM9, fieldVisitor);
this.name = name;
this.values = values;
this.type = type;
}
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (descriptor.equals(Type.getDescriptor(Entry.class))) {
return new MetaGatheringAnnotationVisitor(super.visitAnnotation(descriptor, visible));
}
return super.visitAnnotation(descriptor, visible);
}
public class MetaGatheringAnnotationVisitor extends AnnotationVisitor {
private double min = Double.NEGATIVE_INFINITY;
private double max = Double.POSITIVE_INFINITY;
protected MetaGatheringAnnotationVisitor(AnnotationVisitor annotationVisitor) {
super(ASM9, annotationVisitor);
}
@Override
public void visit(String name, Object value) {
super.visit(name, value);
switch (name) {
case "min" -> {
min = (Double)value;
}
case "max" -> {
max = (Double)value;
}
case "width" -> {}
default -> throw new IllegalArgumentException("Unexpected name in @Entry annotation: " + name);
}
}
@Override
public void visitEnd() {
super.visitEnd();
values.add(new DiscoveredValue(name, min, max, type));
}
}
}

View File

@ -1,32 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.asm;
import io.gitlab.jfronny.libjf.config.api.v1.Preset;
import io.gitlab.jfronny.libjf.config.api.v1.Verifier;
import org.objectweb.asm.*;
import java.util.List;
import static org.objectweb.asm.Opcodes.*;
public class MethodMetaGatheringVisitor extends MethodVisitor {
private final String name;
private final List<String> presets;
private final List<String> verifiers;
protected MethodMetaGatheringVisitor(MethodVisitor methodVisitor, String name, List<String> presets, List<String> verifiers) {
super(ASM9, methodVisitor);
this.name = name;
this.presets = presets;
this.verifiers = verifiers;
}
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (descriptor.equals(Type.getDescriptor(Preset.class))) {
presets.add(name);
}
if (descriptor.equals(Type.getDescriptor(Verifier.class))) {
verifiers.add(name);
}
return super.visitAnnotation(descriptor, visible);
}
}

View File

@ -1,7 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.asm;
public class NotAConfigClassException extends RuntimeException {
public NotAConfigClassException() {
super("This class is not a config class and should not be modified");
}
}

View File

@ -1,5 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.asm;
public enum TransformerMode {
CONFIG_ROOT, CONFIG_CATEGORY, OTHER
}

View File

@ -1,96 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.asm.value;
import io.gitlab.jfronny.libjf.config.plugin.asm.ConfigInjectClassTransformer;
import io.gitlab.jfronny.libjf.config.plugin.util.GeneratorAdapter2;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;
import static io.gitlab.jfronny.libjf.config.plugin.asm.value.KnownTypes.*;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Type.*;
public class DiscoveredValue {
private static final String PREFIX = ConfigInjectClassTransformer.PREFIX;
public final String name;
public final double min;
public final double max;
public final Type aType;
public final KnownType type;
public DiscoveredValue(String name, double min, double max, Type type) {
this.name = name;
this.min = min;
this.max = max;
this.aType = type;
this.type = KnownType.of(type);
}
public void generateRegistration(GeneratorAdapter2 m, ConfigInjectClassTransformer t) {
if (type != KnownType.OBJECT) {
m.push(name);
m.getStatic(t.current, name, aType);
if (!aType.equals(type.unboxed)) {
m.comment("Unboxing value " + name + " from " + aType + " to " + type.unboxed);
m.unbox(type.unboxed); // Unbox (as parameter is unboxed) or leave as is (if target is unboxed)
}
}
switch (type) {
case INT, LONG, FLOAT, DOUBLE -> {
m.comment("Numeric type");
m.push(min);
m.push(max);
m.supplier(t.current.getInternalName(), gName(), gDesc());
m.consumer(t.current.getInternalName(), sName(), sDesc());
t.dslInvoke(m, "value", CATEGORY_BUILDER_TYPE, STRING_TYPE, type.unboxed, DOUBLE_TYPE, DOUBLE_TYPE, SUPPLIER_TYPE, CONSUMER_TYPE);
}
case BOOLEAN, STRING -> {
m.comment("Simple one-value type");
m.supplier(t.current.getInternalName(), gName(), gDesc());
m.consumer(t.current.getInternalName(), sName(), sDesc());
t.dslInvoke(m, "value", CATEGORY_BUILDER_TYPE, STRING_TYPE, type.unboxed, SUPPLIER_TYPE, CONSUMER_TYPE);
}
case OBJECT -> {
System.err.println("WARNING: Attempted to use unsupported type in config. The entry \"" + name + "\" will fall back to reflective runtime access!");
m.comment("Reflective access due to missing compatibility for the type \"" + aType + "\"");
m.push(t.current);
m.push(name);
m.invokeIStatic(ENTRY_INFO_TYPE, new Method("ofField", ENTRY_INFO_TYPE, new Type[]{CLASS_TYPE, STRING_TYPE}));
m.invokeInterface(CATEGORY_BUILDER_TYPE, new Method("value", CATEGORY_BUILDER_TYPE, new Type[]{ENTRY_INFO_TYPE}));
}
}
}
public void generateλ(ConfigInjectClassTransformer t) {
GeneratorAdapter2 m = t.method(ACC_PRIVATE | ACC_STATIC, gName(), gDesc().getInternalName(), null, null);
m.getStatic(t.current, name, aType);
m.comment("Boxing from " + aType + " (" + aType.getSort() + ")");
m.box(aType); // Box if target field uses unboxed value
m.returnValue();
m.endMethod();
m = t.method(ACC_PRIVATE | ACC_STATIC, sName(), sDesc().getInternalName(), null, null);
m.loadArg(0);
m.unbox(aType); // Unbox to the target fields type
m.putStatic(t.current, name, aType);
m.returnValue();
m.endMethod();
}
private String gName() {
return PREFIX + "get$" + name;
}
private Type gDesc() {
return getMethodType(type.boxed);
}
private String sName() {
return PREFIX + "set$" + name;
}
private Type sDesc() {
return getMethodType(VOID_TYPE, type.boxed);
}
}

View File

@ -1,41 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.asm.value;
import org.objectweb.asm.Type;
import java.util.HashMap;
import java.util.Map;
import static io.gitlab.jfronny.libjf.config.plugin.asm.value.KnownTypes.*;
import static org.objectweb.asm.Type.*;
public enum KnownType {
INT(INT_BOX_TYPE, INT_TYPE),
LONG(LONG_BOX_TYPE, LONG_TYPE),
FLOAT(FLOAT_BOX_TYPE, FLOAT_TYPE),
DOUBLE(DOUBLE_BOX_TYPE, DOUBLE_TYPE),
STRING(STRING_TYPE, STRING_TYPE),
BOOLEAN(BOOLEAN_BOX_TYPE, BOOLEAN_TYPE),
OBJECT(OBJECT_TYPE, OBJECT_TYPE);
public final Type boxed;
public final Type unboxed;
private static final Map<Type, KnownType> t2t = new HashMap<>();
KnownType(Type boxed, Type unboxed) {
this.boxed = boxed;
this.unboxed = unboxed;
}
static {
for (KnownType value : values()) {
if (value != OBJECT) {
t2t.put(value.boxed, value);
t2t.put(value.unboxed, value);
}
}
}
public static KnownType of(Type type) {
return t2t.getOrDefault(type, OBJECT);
}
}

View File

@ -1,38 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.asm.value;
import io.gitlab.jfronny.libjf.config.api.v1.*;
import io.gitlab.jfronny.libjf.config.api.v1.dsl.*;
import org.objectweb.asm.Type;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class KnownTypes {
// DSL types
public static final Type CONFIG_BUILDER_TYPE = Type.getType(ConfigBuilder.class);
public static final Type CONFIG_BUILDER_FUNCTION_TYPE = Type.getType(ConfigBuilder.ConfigBuilderFunction.class);
public static final Type CATEGORY_BUILDER_TYPE = Type.getType(CategoryBuilder.class);
public static final Type CATEGORY_BUILDER_FUNCTION_TYPE = Type.getType(CategoryBuilder.CategoryBuilderFunction.class);
public static final Type DSL_TYPE = Type.getType(DSL.class);
public static final Type DSL_DEFAULTED_TYPE = Type.getType(DSL.Defaulted.class);
public static final Type CONFIG_HOLDER_TYPE = Type.getType(ConfigHolder.class);
public static final Type CONFIG_INSTANCE_TYPE = Type.getType(ConfigInstance.class);
public static final Type ENTRY_INFO_TYPE = Type.getType(EntryInfo.class);
// Boxes
public static final Type INT_BOX_TYPE = Type.getType(Integer.class);
public static final Type LONG_BOX_TYPE = Type.getType(Long.class);
public static final Type FLOAT_BOX_TYPE = Type.getType(Float.class);
public static final Type DOUBLE_BOX_TYPE = Type.getType(Double.class);
public static final Type BOOLEAN_BOX_TYPE = Type.getType(Boolean.class);
public static final Type CHARACTER_BOX_TYPE = Type.getType(Character.class);
public static final Type SHORT_BOX_TYPE = Type.getType(Short.class);
public static final Type BYTE_BOX_TYPE = Type.getType(Byte.class);
// Additional
public static final Type STRING_TYPE = Type.getType(String.class);
public static final Type OBJECT_TYPE = Type.getType(Object.class);
public static final Type RUNNABLE_TYPE = Type.getType(Runnable.class);
public static final Type SUPPLIER_TYPE = Type.getType(Supplier.class);
public static final Type CONSUMER_TYPE = Type.getType(Consumer.class);
public static final Type NUMBER_TYPE = Type.getType(Number.class);
public static final Type CLASS_TYPE = Type.getType(Class.class);
}

View File

@ -1,39 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.fmj;
import io.gitlab.jfronny.gson.*;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.libjf.config.impl.ConfigCore;
import io.gitlab.jfronny.libjf.config.plugin.asm.ConfigInjectClassTransformer;
import org.objectweb.asm.Type;
import java.util.Iterator;
import java.util.Set;
public class FabricModJsonTransformer {
private static final Gson INPUT_GSON = new GsonBuilder().setLenient().create();
private static final Gson OUTPUT_GSON = new GsonBuilder().create();
private static final String ENTRYPOINTS = "entrypoints";
private static final String LIBJF_CONFIG = ConfigCore.MODULE_ID;
public static void transform(JsonReader reader, JsonWriter writer, Set<Type> configClasses) {
JsonObject fmj = INPUT_GSON.<JsonElement>fromJson(reader, JsonElement.class).getAsJsonObject();
if (!fmj.has(ENTRYPOINTS)) fmj.add(ENTRYPOINTS, new JsonObject());
JsonObject entrypoints = fmj.get(ENTRYPOINTS).getAsJsonObject();
if (!entrypoints.has(LIBJF_CONFIG)) entrypoints.add(LIBJF_CONFIG, new JsonArray());
JsonArray libjfConfig = entrypoints.getAsJsonArray(LIBJF_CONFIG).getAsJsonArray();
for (Type klazz : configClasses) {
// Remove references to class
Iterator<JsonElement> each = libjfConfig.iterator();
while(each.hasNext()) {
JsonElement element = each.next();
if (element.isJsonPrimitive() && element.getAsString().equals(klazz.getClassName())) {
each.remove();
}
}
// Add reference to init method
libjfConfig.add(klazz.getClassName() + "::" + ConfigInjectClassTransformer.REGISTER_METHOD);
}
OUTPUT_GSON.toJson(fmj, writer);
}
}

View File

@ -1,78 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.util;
import org.gradle.api.file.FileTreeElement;
import org.gradle.api.file.RelativePath;
import org.gradle.api.internal.file.DefaultFileTreeElement;
import java.io.*;
public class ArchiveFileTreeElement implements FileTreeElement {
private final RelativeArchivePath archivePath;
public ArchiveFileTreeElement(RelativeArchivePath archivePath) {
this.archivePath = archivePath;
}
public boolean isClassFile() {
return archivePath.isClassFile();
}
@Override
public File getFile() {
return null;
}
@Override
public boolean isDirectory() {
return archivePath.entry.isDirectory();
}
@Override
public long getLastModified() {
return archivePath.entry.getLastModifiedDate().getTime();
}
@Override
public long getSize() {
return archivePath.entry.getSize();
}
@Override
public InputStream open() {
return null;
}
@Override
public void copyTo(OutputStream outputStream) {
}
@Override
public boolean copyTo(File file) {
return false;
}
@Override
public String getName() {
return archivePath.getPathString();
}
@Override
public String getPath() {
return archivePath.getLastName();
}
@Override
public RelativeArchivePath getRelativePath() {
return archivePath;
}
@Override
public int getMode() {
return archivePath.entry.getUnixMode();
}
public FileTreeElement asFileTreeElement() {
return new DefaultFileTreeElement(null, new RelativePath(!isDirectory(), archivePath.getSegments()), null, null);
}
}

View File

@ -1,26 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.util;
import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.*;
public class ClInitInjectVisitor extends MethodVisitor {
private final String owner;
private final String name;
private final String descriptor;
public ClInitInjectVisitor(MethodVisitor mw, String owner, String name, String descriptor) {
super(ASM9, mw);
this.owner = owner;
this.name = name;
this.descriptor = descriptor;
}
@Override
public void visitInsn(int opcode) {
if (opcode == RETURN) {
super.visitMethodInsn(INVOKESTATIC, owner, name, descriptor, false);
}
super.visitInsn(opcode);
}
}

View File

@ -1,30 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.util;
import org.apache.tools.zip.Zip64Mode;
import org.apache.tools.zip.ZipOutputStream;
import org.gradle.api.UncheckedIOException;
import java.io.File;
public class DefaultZipCompressor implements ZipCompressor {
private final int entryCompressionMethod;
private final Zip64Mode zip64Mode;
public DefaultZipCompressor(boolean allowZip64Mode, int entryCompressionMethod) {
this.entryCompressionMethod = entryCompressionMethod;
this.zip64Mode = allowZip64Mode ? Zip64Mode.AsNeeded : Zip64Mode.Never;
}
@Override
public ZipOutputStream createArchiveOutputStream(File destination) {
try {
ZipOutputStream zipOutputStream = new ZipOutputStream(destination);
zipOutputStream.setUseZip64(zip64Mode);
zipOutputStream.setMethod(entryCompressionMethod);
return zipOutputStream;
} catch (Exception e) {
String message = String.format("Unable to create ZIP output stream for file %s.", destination);
throw new UncheckedIOException(message, e);
}
}
}

View File

@ -1,36 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.util;
import java.io.IOException;
import java.io.OutputStream;
public class DelegatingUncloseableOutputStream extends OutputStream {
private final OutputStream delegate;
public DelegatingUncloseableOutputStream(OutputStream delegate) {
super();
this.delegate = delegate;
}
@Override
public void write(int i) throws IOException {
delegate.write(i);
}
@Override
public void write(byte[] b) throws IOException {
delegate.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
delegate.write(b, off, len);
}
@Override
public void flush() {
}
@Override
public void close() {
}
}

View File

@ -1,35 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.util;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class EnumerationSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
private final Enumeration<T> enumeration;
public EnumerationSpliterator(long est, int additionalCharacteristics, Enumeration<T> enumeration) {
super(est, additionalCharacteristics);
this.enumeration = enumeration;
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
if (enumeration.hasMoreElements()) {
action.accept(enumeration.nextElement());
return true;
}
return false;
}
@Override
public void forEachRemaining(Consumer<? super T> action) {
while (enumeration.hasMoreElements())
action.accept(enumeration.nextElement());
}
public static <T> Stream<T> stream(Enumeration<T> enumeration) {
EnumerationSpliterator<T> spliterator = new EnumerationSpliterator<>(Long.MAX_VALUE, Spliterator.ORDERED, enumeration);
return StreamSupport.stream(spliterator, false);
}
}

View File

@ -1,123 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.util;
import io.gitlab.jfronny.libjf.config.plugin.BuildMetadata;
import io.gitlab.jfronny.libjf.config.plugin.asm.value.KnownTypes;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import java.lang.invoke.LambdaMetafactory;
import static org.objectweb.asm.Opcodes.ASM9;
import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
public class GeneratorAdapter2 extends GeneratorAdapter {
private static final Handle metafactory = new Handle(
H_INVOKESTATIC,
Type.getInternalName(LambdaMetafactory.class),
"metafactory",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",
false
);
public GeneratorAdapter2(MethodVisitor methodVisitor, int access, String name, String descriptor) {
super(ASM9, methodVisitor, access, name, descriptor);
}
public void λ(String lambdaMethodName, String lambdaType, Type lambdaDescriptor, Type implDescriptor, String implOwner, String implName) {
invokeDynamic(lambdaMethodName,
lambdaType,
metafactory,
lambdaDescriptor,
new Handle(H_INVOKESTATIC, implOwner, implName, implDescriptor.getInternalName(), false),
implDescriptor);
}
public void λ(String lambdaMethodName, String lambdaType, Type descriptor, String implOwner, String implName) {
λ(lambdaMethodName, lambdaType, descriptor, descriptor, implOwner, implName);
}
public void runnable(String implOwner, String implName) {
λ("run",
"()Ljava/lang/Runnable;",
Type.getType("()V"),
implOwner,
implName);
}
public void supplier(String implOwner, String implName, Type implDescriptor) {
λ("get",
"()Ljava/util/function/Supplier;",
Type.getType("()Ljava/lang/Object;"),
implDescriptor,
implOwner,
implName);
}
public void consumer(String implOwner, String implName, Type implDescriptor) {
λ("accept",
"()Ljava/util/function/Consumer;",
Type.getType("(Ljava/lang/Object;)V"),
implDescriptor,
implOwner,
implName);
}
public void box(Type type) {
Type boxedType;
switch (type.getSort()) {
case Type.VOID:
return;
case Type.CHAR:
boxedType = KnownTypes.CHARACTER_BOX_TYPE;
break;
case Type.BOOLEAN:
boxedType = KnownTypes.BOOLEAN_BOX_TYPE;
break;
case Type.DOUBLE:
boxedType = KnownTypes.DOUBLE_BOX_TYPE;
break;
case Type.FLOAT:
boxedType = KnownTypes.FLOAT_BOX_TYPE;
break;
case Type.LONG:
boxedType = KnownTypes.LONG_BOX_TYPE;
break;
case Type.INT:
boxedType = KnownTypes.INT_BOX_TYPE;
break;
case Type.SHORT:
boxedType = KnownTypes.SHORT_BOX_TYPE;
break;
case Type.BYTE:
boxedType = KnownTypes.BYTE_BOX_TYPE;
break;
default:
boxedType = null;
break;
}
if (boxedType == null) {
comment("Unkown boxed type, simply casting");
checkCast(type);
} else {
comment("Boxing " + type + " to " + boxedType);
invokeStatic(boxedType, new Method("valueOf", boxedType, new Type[]{type}));
}
}
public void invokeIStatic(Type owner, Method method) {
invokeInsn(Opcodes.INVOKESTATIC, owner, method, true);
}
public void comment(String comment) {
if (BuildMetadata.IS_RELEASE) return;
push("[Comment/Generator] " + comment);
pop();
}
// Taken from GeneratorAdapter
private void invokeInsn(final int opcode, final Type type, final Method method, final boolean isInterface) {
String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName();
mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor(), isInterface);
}
}

View File

@ -1,21 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.util;
import org.apache.tools.zip.ZipOutputStream;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.bundling.ZipEntryCompression;
import org.gradle.util.GradleVersion;
public class GradleVersionUtil {
private final GradleVersion version;
public GradleVersionUtil(String version) {
this.version = GradleVersion.version(version);
}
public ZipCompressor getInternalCompressor(ZipEntryCompression entryCompression, Jar jar) {
return switch (entryCompression) {
case DEFLATED -> new DefaultZipCompressor(jar.isZip64(), ZipOutputStream.DEFLATED);
case STORED -> new DefaultZipCompressor(jar.isZip64(), ZipOutputStream.STORED);
};
}
}

View File

@ -1,32 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.util;
import org.apache.tools.zip.ZipEntry;
import org.gradle.api.file.RelativePath;
import java.util.Arrays;
import java.util.List;
public class RelativeArchivePath extends RelativePath {
public ZipEntry entry;
public RelativeArchivePath(ZipEntry entry) {
super(!entry.isDirectory(), entry.getName().split("/"));
this.entry = entry;
}
public boolean isClassFile() {
return getLastName().endsWith(".class");
}
@Override
public RelativeArchivePath getParent() {
List<String> segments = Arrays.asList(getSegments());
if (segments.size() == 1) {
return null;
} else {
//Parent is always a directory so add / to the end of the path
String path = String.join("/", segments.subList(0, segments.size() - 1)) + "/";
return new RelativeArchivePath(new ZipEntry(path));
}
}
}

View File

@ -1,11 +0,0 @@
package io.gitlab.jfronny.libjf.config.plugin.util;
import org.apache.tools.zip.ZipOutputStream;
import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory;
import java.io.File;
public interface ZipCompressor extends ArchiveOutputStreamFactory {
ZipOutputStream createArchiveOutputStream(File destination);
}

View File

@ -21,5 +21,4 @@ include("libjf-translate-v1")
include("libjf-unsafe-v0")
include("libjf-web-v0")
include("libjf-config-compiler-plugin")
include("libjf-config-compiler-plugin-v2")