[config-compiler-plugin] Keep original bytes for non-config classes

This commit is contained in:
Johannes Frohnmeyer 2022-08-28 11:43:46 +02:00
parent f434e8843a
commit 55bfa434ec
Signed by: Johannes
GPG Key ID: E76429612C2929F4
5 changed files with 69 additions and 35 deletions

View File

@ -27,3 +27,10 @@ allprojects {
compileOnly("io.gitlab.jfronny:commons-gson:$rootProject.commons_version") compileOnly("io.gitlab.jfronny:commons-gson:$rootProject.commons_version")
} }
} }
task copyVersionNumber {
doLast {
java.awt.Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new java.awt.datatransfer.StringSelection("$version"), null)
println("Copied version number: $version")
}
}

View File

@ -20,7 +20,6 @@ dependencies {
implementation("io.gitlab.jfronny:commons-gson:$rootProject.commons_version") implementation("io.gitlab.jfronny:commons-gson:$rootProject.commons_version")
implementation("org.ow2.asm:asm:9.3") implementation("org.ow2.asm:asm:9.3")
implementation("org.ow2.asm:asm-commons:9.3") implementation("org.ow2.asm:asm-commons:9.3")
implementation("org.ow2.asm:asm-util:9.3")
implementation(project(":libjf-config-core-v1")) { implementation(project(":libjf-config-core-v1")) {
transitive(false) transitive(false)
} }

View File

@ -3,16 +3,17 @@ package io.gitlab.jfronny.libjf.config.plugin;
import io.gitlab.jfronny.gson.stream.JsonReader; import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter; import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.libjf.config.plugin.asm.ConfigInjectClassTransformer; 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.fmj.FabricModJsonTransformer;
import io.gitlab.jfronny.libjf.config.plugin.util.*; import io.gitlab.jfronny.libjf.config.plugin.util.*;
import org.apache.tools.zip.*; import org.apache.tools.zip.*;
import org.gradle.api.GradleException; import org.gradle.api.GradleException;
import org.gradle.api.file.FileCopyDetails; import org.gradle.api.file.FileCopyDetails;
import org.objectweb.asm.*; import org.objectweb.asm.*;
import org.objectweb.asm.util.CheckClassAdapter;
import java.io.*; import java.io.*;
import java.util.*; import java.util.GregorianCalendar;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
public class StreamAction { public class StreamAction {
@ -39,7 +40,7 @@ public class StreamAction {
} else { } else {
return visitFile(details); return visitFile(details);
} }
} catch (Exception e) { } catch (Throwable e) {
throw new GradleException("Could not add " + details + " to ZIP " + zipFile, e); throw new GradleException("Could not add " + details + " to ZIP " + zipFile, e);
} }
} }
@ -103,7 +104,7 @@ public class StreamAction {
copyArchiveEntry(archiveFilePath, archive); copyArchiveEntry(archiveFilePath, archive);
} }
return false; return false;
} catch (IOException e) { } catch (Throwable e) {
throw new GradleException("Could not read archive entry " + archiveFilePath.getPathString(), e); throw new GradleException("Could not read archive entry " + archiveFilePath.getPathString(), e);
} }
} }
@ -136,25 +137,29 @@ public class StreamAction {
private void processClass(RelativeArchivePath file, ZipFile archive) throws IOException { private void processClass(RelativeArchivePath file, ZipFile archive) throws IOException {
ZipEntry zipEntry = new ZipEntry(file.getPathString()); ZipEntry zipEntry = new ZipEntry(file.getPathString());
addParentDirectories(new RelativeArchivePath(zipEntry)); addParentDirectories(new RelativeArchivePath(zipEntry));
processClass(archive.getInputStream(file.entry), file.getPathString(), file.entry.getTime()); processClass(archive.getInputStream(file.entry).readAllBytes(), file.getPathString(), file.entry.getTime());
} }
private void processClass(FileCopyDetails details) throws IOException { private void processClass(FileCopyDetails details) throws IOException {
try (InputStream is = new BufferedInputStream(new FileInputStream(details.getFile()))) { try (InputStream is = new FileInputStream(details.getFile())) {
processClass(is, details.getPath(), details.getLastModified()); processClass(is.readAllBytes(), details.getPath(), details.getLastModified());
} }
} }
private void processClass(InputStream classInputStream, String path, long lastModified) throws IOException { private void processClass(byte[] klazz, String path, long lastModified) throws IOException {
final ClassReader reader = new ClassReader(classInputStream); try {
final ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES); final ClassReader reader = new ClassReader(klazz);
reader.accept(new CheckClassAdapter( final ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
new ConfigInjectClassTransformer(modId, writer, knownConfigClasses) final ConfigInjectClassTransformer transformer = new ConfigInjectClassTransformer(modId, writer, knownConfigClasses);
), ClassReader.EXPAND_FRAMES); reader.accept(transformer, ClassReader.EXPAND_FRAMES);
klazz = writer.toByteArray();
} catch (NotAConfigClassException notAConfigClass) {
// Use original bytes
}
ZipEntry archiveEntry = new ZipEntry(path); ZipEntry archiveEntry = new ZipEntry(path);
archiveEntry.setTime(getArchiveTimeFor(lastModified)); archiveEntry.setTime(getArchiveTimeFor(lastModified));
zipOutStr.putNextEntry(archiveEntry); zipOutStr.putNextEntry(archiveEntry);
zipOutStr.write(writer.toByteArray()); zipOutStr.write(klazz);
zipOutStr.closeEntry(); zipOutStr.closeEntry();
} }
@ -165,7 +170,7 @@ public class StreamAction {
} }
private void processFMJ(FileCopyDetails details) throws IOException { private void processFMJ(FileCopyDetails details) throws IOException {
try (InputStream is = new BufferedInputStream(new FileInputStream(details.getFile()))) { try (InputStream is = new FileInputStream(details.getFile())) {
processFMJ(is, details.getPath(), details.getLastModified()); processFMJ(is, details.getPath(), details.getLastModified());
} }
} }

View File

@ -35,7 +35,23 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
private final List<String> verifiers = new LinkedList<>(); private final List<String> verifiers = new LinkedList<>();
private final List<DiscoveredValue> values = new LinkedList<>(); private final List<DiscoveredValue> values = new LinkedList<>();
private boolean initFound = false; private boolean initFound = false;
private TransformerMode mode = TransformerMode.OTHER; 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) { public ConfigInjectClassTransformer(String modId, ClassVisitor cw, Set<Type> knownConfigClasses) {
super(ASM9, cw); super(ASM9, cw);
@ -52,29 +68,31 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
@Override @Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (descriptor.equals(Type.getDescriptor(Category.class)) && mode == TransformerMode.OTHER) { if (mode == null) throw new IllegalStateException("Attempted to visit annotation before class");
if (descriptor.equals(Type.getDescriptor(Category.class)) && isOther()) {
mode = TransformerMode.CONFIG_CATEGORY; mode = TransformerMode.CONFIG_CATEGORY;
return new AnnotationMetaGatheringVisitor(super.visitAnnotation(descriptor, visible), referencedConfigs); return new AnnotationMetaGatheringVisitor(super.visitAnnotation(descriptor, visible), referencedConfigs);
} }
if (descriptor.equals(Type.getDescriptor(JfConfig.class)) && mode == TransformerMode.OTHER) { else if (descriptor.equals(Type.getDescriptor(JfConfig.class)) && isOther()) {
mode = TransformerMode.CONFIG_ROOT; mode = TransformerMode.CONFIG_ROOT;
knownConfigClasses.add(current); knownConfigClasses.add(current);
return new AnnotationMetaGatheringVisitor(super.visitAnnotation(descriptor, visible), referencedConfigs); return new AnnotationMetaGatheringVisitor(super.visitAnnotation(descriptor, visible), referencedConfigs);
} else {
return super.visitAnnotation(descriptor, visible);
} }
return super.visitAnnotation(descriptor, visible);
} }
@Override @Override
public void visitNestMember(String nestMember) { public void visitNestMember(String nestMember) {
if (mode != TransformerMode.OTHER) { ensureCategory();
categories.add(nestMember); categories.add(nestMember);
}
super.visitNestMember(nestMember); super.visitNestMember(nestMember);
} }
@Override @Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if (mode == TransformerMode.CONFIG_ROOT) { ensureCategory();
if (isConfig()) {
if ("<clinit>".equals(name)) { if ("<clinit>".equals(name)) {
initFound = true; initFound = true;
return new ClInitInjectVisitor( return new ClInitInjectVisitor(
@ -85,10 +103,8 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
); );
} }
} }
if (mode != TransformerMode.OTHER) { if (name.startsWith(PREFIX)) {
if (name.startsWith(PREFIX)) { throw new GradleException("This class declares methods generated by this plugin manually. Do not transform classes twice!");
throw new GradleException("This class declares methods generated by this plugin manually. Do not transform classes twice!");
}
} }
if ((access & ACC_STATIC) == ACC_STATIC) { if ((access & ACC_STATIC) == ACC_STATIC) {
// Possibly add verifier or preset // Possibly add verifier or preset
@ -101,6 +117,7 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
@Override @Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
ensureCategory();
if ((access & ACC_STATIC) == ACC_STATIC) { if ((access & ACC_STATIC) == ACC_STATIC) {
return new FieldMetaGatheringVisitor(super.visitField(access, name, descriptor, signature, value), name, values, Type.getType(descriptor)); return new FieldMetaGatheringVisitor(super.visitField(access, name, descriptor, signature, value), name, values, Type.getType(descriptor));
} }
@ -109,7 +126,8 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
@Override @Override
public void visitEnd() { public void visitEnd() {
if (mode == TransformerMode.CONFIG_ROOT) { ensureCategory();
if (isConfig()) {
if (!initFound) { if (!initFound) {
// Generate <clinit> if missing // Generate <clinit> if missing
GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, CLINIT, "()V", null, null); GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, CLINIT, "()V", null, null);
@ -150,9 +168,8 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
m.endMethod(); m.endMethod();
} }
} }
if (mode != TransformerMode.OTHER) { {
boolean root = mode == TransformerMode.CONFIG_ROOT; Type builderType = isConfig() ? CONFIG_BUILDER_TYPE : CATEGORY_BUILDER_TYPE;
Type builderType = root ? CONFIG_BUILDER_TYPE : CATEGORY_BUILDER_TYPE;
GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, BUILDER_ROOT, getMethodDescriptor(builderType, builderType), null, null); GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, BUILDER_ROOT, getMethodDescriptor(builderType, builderType), null, null);
m.loadArg(0); m.loadArg(0);
for (String name : referencedConfigs) { for (String name : referencedConfigs) {
@ -195,10 +212,9 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
} }
public void dslInvoke(GeneratorAdapter m, String name, Type returnType, Type... arguments) { public void dslInvoke(GeneratorAdapter m, String name, Type returnType, Type... arguments) {
boolean root = mode == TransformerMode.CONFIG_ROOT; Type builderType = isConfig() ? CONFIG_BUILDER_TYPE : CATEGORY_BUILDER_TYPE;
Type builderType = root ? CONFIG_BUILDER_TYPE : CATEGORY_BUILDER_TYPE;
m.invokeInterface(builderType, new Method(name, returnType, arguments)); m.invokeInterface(builderType, new Method(name, returnType, arguments));
if (root) m.checkCast(CONFIG_BUILDER_TYPE); if (isConfig()) m.checkCast(CONFIG_BUILDER_TYPE);
} }
private String camelCase(String s) { private String camelCase(String s) {

View File

@ -0,0 +1,7 @@
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");
}
}