[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")
}
}
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("org.ow2.asm:asm:9.3")
implementation("org.ow2.asm:asm-commons:9.3")
implementation("org.ow2.asm:asm-util:9.3")
implementation(project(":libjf-config-core-v1")) {
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.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 org.objectweb.asm.util.CheckClassAdapter;
import java.io.*;
import java.util.*;
import java.util.GregorianCalendar;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
public class StreamAction {
@ -39,7 +40,7 @@ public class StreamAction {
} else {
return visitFile(details);
}
} catch (Exception e) {
} catch (Throwable e) {
throw new GradleException("Could not add " + details + " to ZIP " + zipFile, e);
}
}
@ -103,7 +104,7 @@ public class StreamAction {
copyArchiveEntry(archiveFilePath, archive);
}
return false;
} catch (IOException e) {
} catch (Throwable 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 {
ZipEntry zipEntry = new ZipEntry(file.getPathString());
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 {
try (InputStream is = new BufferedInputStream(new FileInputStream(details.getFile()))) {
processClass(is, details.getPath(), details.getLastModified());
try (InputStream is = new FileInputStream(details.getFile())) {
processClass(is.readAllBytes(), details.getPath(), details.getLastModified());
}
}
private void processClass(InputStream classInputStream, String path, long lastModified) throws IOException {
final ClassReader reader = new ClassReader(classInputStream);
final ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
reader.accept(new CheckClassAdapter(
new ConfigInjectClassTransformer(modId, writer, knownConfigClasses)
), ClassReader.EXPAND_FRAMES);
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();
} catch (NotAConfigClassException notAConfigClass) {
// Use original bytes
}
ZipEntry archiveEntry = new ZipEntry(path);
archiveEntry.setTime(getArchiveTimeFor(lastModified));
zipOutStr.putNextEntry(archiveEntry);
zipOutStr.write(writer.toByteArray());
zipOutStr.write(klazz);
zipOutStr.closeEntry();
}
@ -165,7 +170,7 @@ public class StreamAction {
}
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());
}
}

View File

@ -35,7 +35,23 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
private final List<String> verifiers = new LinkedList<>();
private final List<DiscoveredValue> values = new LinkedList<>();
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) {
super(ASM9, cw);
@ -52,29 +68,31 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
@Override
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;
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;
knownConfigClasses.add(current);
return new AnnotationMetaGatheringVisitor(super.visitAnnotation(descriptor, visible), referencedConfigs);
} else {
return super.visitAnnotation(descriptor, visible);
}
return super.visitAnnotation(descriptor, visible);
}
@Override
public void visitNestMember(String nestMember) {
if (mode != TransformerMode.OTHER) {
categories.add(nestMember);
}
ensureCategory();
categories.add(nestMember);
super.visitNestMember(nestMember);
}
@Override
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)) {
initFound = true;
return new ClInitInjectVisitor(
@ -85,10 +103,8 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
);
}
}
if (mode != TransformerMode.OTHER) {
if (name.startsWith(PREFIX)) {
throw new GradleException("This class declares methods generated by this plugin manually. Do not transform classes twice!");
}
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
@ -101,6 +117,7 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
@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));
}
@ -109,7 +126,8 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
@Override
public void visitEnd() {
if (mode == TransformerMode.CONFIG_ROOT) {
ensureCategory();
if (isConfig()) {
if (!initFound) {
// Generate <clinit> if missing
GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, CLINIT, "()V", null, null);
@ -150,9 +168,8 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
m.endMethod();
}
}
if (mode != TransformerMode.OTHER) {
boolean root = mode == TransformerMode.CONFIG_ROOT;
Type builderType = root ? CONFIG_BUILDER_TYPE : CATEGORY_BUILDER_TYPE;
{
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) {
@ -195,10 +212,9 @@ public class ConfigInjectClassTransformer extends ClassVisitor {
}
public void dslInvoke(GeneratorAdapter m, String name, Type returnType, Type... arguments) {
boolean root = mode == TransformerMode.CONFIG_ROOT;
Type builderType = root ? CONFIG_BUILDER_TYPE : CATEGORY_BUILDER_TYPE;
Type builderType = isConfig() ? CONFIG_BUILDER_TYPE : CATEGORY_BUILDER_TYPE;
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) {

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");
}
}