Skip to content

Commit

Permalink
注解处理器
Browse files Browse the repository at this point in the history
Took 38 hours 33 minutes
  • Loading branch information
xkball committed Oct 13, 2024
1 parent c0c2222 commit 0bfcbe7
Show file tree
Hide file tree
Showing 142 changed files with 5,404 additions and 44 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,18 @@ The contents of this repository are licensed as follows:
- in the case of original source code from tin_tea_tech or compiled artifacts generated from it, under LGPL-3.0.
- in the case of original art assets from tin_tea_tech, under CC BY-NC 4.0.
- Special case: The art assets under the namespace tectech come from https://github.com/GTNewHorizons/TecTech/tree/master, using the MIT license


## 构建 Build

请添加如下jvm参数,或者解除build.gradle文件181行到189行注释.

Please add the following jvm args or uncomment lines 181 to 189 of build.gradle.

```
--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
```
83 changes: 83 additions & 0 deletions TinTeaTechAnnotationProcessor/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '8.1.1'
}

tasks.register('generateAnnotationProcessorServicesFile'){

var target = file("src/main/resources/META-INF/services/javax.annotation.processing.Processor")
target.parentFile.mkdirs()
target.createNewFile()
target.text = ""

var classesDir = fileTree("src/main/java/com/xkball/tin_tea_tech/annotation_processor")
var classFiles = classesDir.getFiles().findAll { it.name.endsWith('.java') }
classFiles.each {
var path = it.path
var offset = path.indexOf("com\\xkball\\")
if(offset >= 0) target.text += path.substring(offset,path.length()-5).replace('\\','.') + '\n'
}

inputs.dir file("src/main/java")
outputs.file target
}



tasks.named("processResources") {
dependsOn generateAnnotationProcessorServicesFile
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}

tasks.named("build"){
jar {
archiveBaseName = "TinTeaTech-LocalAnnotationProcessor"
archiveVersion.set(localAnnotationProcessorVersion)
// destinationDirectory = file("../build/local_annotation_processor/lib")
}

}

tasks.named("shadowJar"){
archiveBaseName = "TinTeaTech-LocalAnnotationProcessor"
archiveVersion.set(localAnnotationProcessorVersion)
}

tasks.withType(JavaCompile).configureEach {
getSource().files
// outputs.upToDateWhen { false }
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
// options.fork = true
// options.forkOptions.jvmArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED')
// options.forkOptions.jvmArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED')
// options.forkOptions.jvmArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED')
// options.forkOptions.jvmArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED')
// options.forkOptions.jvmArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED')
// options.forkOptions.jvmArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED')
// options.forkOptions.jvmArgs.add('-ea')


options.compilerArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED')
options.compilerArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED')
options.compilerArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED')
options.compilerArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED')
options.compilerArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED')
options.compilerArgs.add('--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED')
}

repositories {
mavenCentral()
}

dependencies {
implementation 'org.ow2.asm:asm:9.6'
implementation 'org.ow2.asm:asm-tree:9.6'
implementation 'org.ow2.asm:asm-util:9.6'
implementation 'com.github.javaparser:javaparser-core:3.26.2'
implementation 'com.github.javaparser:javaparser-symbol-solver-core:3.26.2'
}

tasks.register("runTest", JavaExec) {
mainClass = "dev.vfyjxf.cumulonimbus.test.SampleJavacPluginTest"
classpath = sourceSets.main.runtimeClasspath
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.xkball.tin_tea_tech.annotation_processor;

import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.tree.JCTree;
import com.xkball.tin_tea_tech.util.CodecManager;
import com.xkball.tin_tea_tech.util.jctree.JCTreeUtils;
import com.xkball.tin_tea_tech.util.StringUtils;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

@SupportedSourceVersion(SourceVersion.RELEASE_21)
@SupportedAnnotationTypes({
CodecProviderProcessor.CODEC_PROVIDER
})
public class CodecProviderProcessor extends AbstractProcessor {

public static final String CODEC_PROVIDER = "com.xkball.tin_tea_tech.api.annotation.CodecProvider";

private ProcessingEnvironment processingEnv;
private Elements elementUtils;
private Filer filer;
private JavacTrees trees;

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
CodecManager.init(processingEnv);
this.processingEnv = processingEnv;
this.elementUtils = processingEnv.getElementUtils();
this.filer = processingEnv.getFiler();
this.trees = JavacTrees.instance(processingEnv);
}

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
JCTreeUtils.setup(processingEnv);
roundEnv.getElementsAnnotatedWith(elementUtils.getTypeElement(CODEC_PROVIDER)).forEach(
anno -> {
var classSymbol = JCTreeUtils.findClassSymbolRecursive((Symbol) anno);
if (classSymbol == null) return;
var fullName = classSymbol.getQualifiedName().toString();
var tree = trees.getTree(elementUtils.getTypeElement(fullName));
handleJCTree(fullName,tree,true);
}
);
return false;
}

private static final Set<String> sourceHandledClasses = new HashSet<>();
private static final Set<String> processorHandledClasses = new HashSet<>();

public static void handleJCTree(String className, JCTree.JCClassDecl tree,boolean inAnnoProcessor){
if(!inAnnoProcessor){
if(sourceHandledClasses.contains(className)) return;
sourceHandledClasses.add(className);
}
else {
if(processorHandledClasses.contains(className)) return;
processorHandledClasses.add(className);
}
JCTreeUtils.findFieldWithAnno(tree,CODEC_PROVIDER)
.forEach(def -> {
if(!JCTreeUtils.isPublicStatic(def)) return;
var codecType = StringUtils.splitTypeNameWithArg(def.vartype.toString());
var typeName = StringUtils.fixTypeNameWithArgRecursive(className,codecType.snd);
var codecRef = className+"."+def.name.toString();
var anno = Objects.requireNonNull(JCTreeUtils.findAnnotation(def,CODEC_PROVIDER));
var codecInsOrder = 0;
var codecInsName = "";
for(var arg : anno.args){
if(arg instanceof JCTree.JCAssign assign){
if(assign.lhs.toString().equals("name")) codecInsName = StringUtils.removeDoubleQuotes(assign.rhs.toString());
if(assign.lhs.toString().equals("order")) codecInsOrder = Integer.parseInt(assign.rhs.toString());
}
}
var data = new CodecManager.CodecProviderData(codecType.fst,typeName,codecRef,codecInsName,codecInsOrder);
if(!CodecManager.NEW_CODEC_PROVIDERS.contains(data)){
if(inAnnoProcessor)
throw new IllegalArgumentException("Can not support codec provider from code generation!!! " + data);
CodecManager.NEW_CODEC_PROVIDERS.add(data);
CodecManager.addCodec(codecType.fst,typeName,codecRef,codecInsName,codecInsOrder);
}
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.xkball.tin_tea_tech.annotation_processor;

import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.List;
import com.xkball.tin_tea_tech.api.annotation.DataField;
import com.xkball.tin_tea_tech.api.annotation.DataSyncAdapter;
import com.xkball.tin_tea_tech.util.CodecManager;
import com.xkball.tin_tea_tech.util.jctree.DataJCTreeUtils;
import com.xkball.tin_tea_tech.util.jctree.JCTreeUtils;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import java.util.ArrayList;


import java.util.Objects;
import java.util.Set;

import static com.xkball.tin_tea_tech.util.jctree.JCTreeUtils.*;

@SupportedSourceVersion(SourceVersion.RELEASE_21)
@SupportedAnnotationTypes({
DataFieldProcessor.DATA_FIELD
})
public class DataFieldProcessor extends AbstractProcessor {

public static final String DATA_FIELD = "com.xkball.tin_tea_tech.api.annotation.DataField";
public static final java.util.List<String> handledClasses = new ArrayList<>();

private ProcessingEnvironment processingEnv;
private Elements elementUtils;
private Filer filer;
private JavacTrees trees;

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
CodecManager.init(processingEnv);
this.processingEnv = processingEnv;
this.elementUtils = processingEnv.getElementUtils();
this.filer = processingEnv.getFiler();
this.trees = JavacTrees.instance(processingEnv);
}

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
JCTreeUtils.setup(processingEnv);
roundEnv.getElementsAnnotatedWith(elementUtils.getTypeElement(DATA_FIELD)).forEach(
anno -> {
var classSymbol = JCTreeUtils.findClassSymbolRecursive((Symbol) anno);
if (classSymbol == null) return;
var fullName = classSymbol.getQualifiedName().toString();
var tree = trees.getTree(elementUtils.getTypeElement(fullName));
handleClass(fullName, tree);
}
);
return false;
}

public static void handleClass(String className, JCTree.JCClassDecl tree) {
if (handledClasses.contains(className)) return;
handledClasses.add(className);
JCTreeUtils.setPos(tree);
var fields = JCTreeUtils.findFieldWithAnno(tree,DATA_FIELD);
var fieldsData = fields.stream().map(f -> DataJCTreeUtils.DataFieldData.fromAnno(className,f)).toList();
var saveList = List.from(fieldsData.toArray(DataJCTreeUtils.DataFieldData[]::new));
var s2cList = List.from(fieldsData.stream().filter(d -> d.syncPolicy() != DataField.SyncPolicy.SyncBoth).toArray(DataJCTreeUtils.DataFieldData[]::new));
var c2sList = List.from(fieldsData.stream().filter(d -> d.syncPolicy() == DataField.SyncPolicy.SyncBoth).toArray(DataJCTreeUtils.DataFieldData[]::new));
tree.defs = tree.defs.append(DataJCTreeUtils.createDataClass(className,"SaveData",saveList));
tree.defs = tree.defs.append(DataJCTreeUtils.createDataClass(className,"Sync2ClientData",s2cList));
tree.defs = tree.defs.append(DataJCTreeUtils.createDataClass(className,"SyncBothData",c2sList));
handleISyncDataOfNBTTag(className,tree,saveList,s2cList,c2sList);
}

public static void handleISyncDataOfNBTTag(String className, JCTree.JCClassDecl tree, List<DataJCTreeUtils.DataFieldData> saveList, List<DataJCTreeUtils.DataFieldData> s2cList, List<DataJCTreeUtils.DataFieldData> c2sList) {
if(tree.implementing.stream().noneMatch(impl -> "ISyncDataOfNBTTag".equals(impl.toString()))) return;
var saveCodecRef = className + ".SaveData.CODEC";
var s2cCodecRef = className + ".Sync2ClientData.CODEC";
var c2sCodecRef = className + ".SyncBothData.CODEC";

var returnCodecType = treeMaker.TypeApply(makeIdent("com.mojang.serialization.Codec"),List.of(treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.UNBOUND),null)));
var saveCodecMethod = treeMaker.MethodDef(mods(Flags.PUBLIC),name("saveCodec"),returnCodecType,List.nil(),List.nil(),List.nil(),treeMaker.Block(0,List.of(treeMaker.Return(makeIdent(saveCodecRef)))),null);
var s2cCodecMethod = treeMaker.MethodDef(mods(Flags.PUBLIC),name("server2clientCodec"),returnCodecType,List.nil(),List.nil(),List.nil(),treeMaker.Block(0,List.of(treeMaker.Return(makeIdent(s2cCodecRef)))),null);
var c2sCodecMethod = treeMaker.MethodDef(mods(Flags.PUBLIC),name("client2serverCodec"),returnCodecType,List.nil(),List.nil(),List.nil(),treeMaker.Block(0,List.of(treeMaker.Return(makeIdent(c2sCodecRef)))),null);
//addAnnotation2Methods("java.lang.Override",List.nil(),saveCodecMethod,s2cCodecMethod,c2sCodecMethod);
tree.defs = tree.defs.appendList(List.of(saveCodecMethod,s2cCodecMethod,c2sCodecMethod));

if(!saveList.isEmpty()){
tree.defs = tree.defs.append(DataJCTreeUtils.createReadMethod(className,"SaveData","load",saveCodecRef,saveList));
tree.defs = tree.defs.append(DataJCTreeUtils.createWriteMethod(className,"SaveData","save",saveCodecRef,saveList));
}
if(!s2cList.isEmpty()){
tree.defs = tree.defs.append(DataJCTreeUtils.createReadMethod(className,"Sync2ClientData","readS2C",s2cCodecRef,s2cList));
tree.defs = tree.defs.append(DataJCTreeUtils.createWriteMethod(className,"Sync2ClientData","writeS2C",s2cCodecRef,s2cList));
}
if(!c2sList.isEmpty()){
tree.defs = tree.defs.append(DataJCTreeUtils.createReadMethod(className,"SyncBothData","readC2S",c2sCodecRef,c2sList));
tree.defs = tree.defs.append(DataJCTreeUtils.createWriteMethod(className,"SyncBothData","writeC2S",c2sCodecRef,c2sList));
}
handleSyncAdapter(className,tree);
}

public static void handleSyncAdapter(String className, JCTree.JCClassDecl tree){
if(JCTreeUtils.findAnnotation(tree,"com.xkball.tin_tea_tech.api.annotation.DataSyncAdapter") == null) return;
var annoArg = Objects.requireNonNull(findAnnotation(tree, "com.xkball.tin_tea_tech.api.annotation.DataSyncAdapter")).args;
var type = DataSyncAdapter.Type.fromString(annoArg.stream().filter(arg -> arg instanceof JCTree.JCAssign assign && "value".equals(assign.lhs.toString())).map(arg -> ((JCTree.JCAssign)arg).rhs.toString()).findFirst().orElseThrow());
if(type == DataSyncAdapter.Type.BlockEntity){
tree.defs = tree.defs.appendList(DataJCTreeUtils.createBlockEntityOverrides());
}
}
}
Loading

0 comments on commit 0bfcbe7

Please sign in to comment.