Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[GAE-Java] Introduce @GrapeSkip to support generated method overloadi…
…ng (#2144) - Introduce Annotation GrapeSkip - Use `Unused` class to support overloading of FFIFuncGen method.
- Loading branch information
1 parent
77b4ec5
commit 3038b89
Showing
9 changed files
with
350 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<artifactId>grape-jdk-parent</artifactId> | ||
<groupId>com.alibaba.graphscope</groupId> | ||
<version>${revision}</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>grape-annotation</artifactId> | ||
|
||
<properties> | ||
<maven.compiler.source>8</maven.compiler.source> | ||
<maven.compiler.target>8</maven.compiler.target> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>com.squareup</groupId> | ||
<artifactId>javapoet</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.alibaba.fastffi</groupId> | ||
<artifactId>annotation-processor</artifactId> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<configuration> | ||
<!-- Disable annotation processing for ourselves --> | ||
<compilerArgument>-proc:none</compilerArgument> | ||
</configuration> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-source-plugin</artifactId> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
</project> |
17 changes: 17 additions & 0 deletions
17
...gine/java/grape-annotation/src/main/java/com/alibaba/graphscope/annotation/GrapeSkip.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.alibaba.graphscope.annotation; | ||
|
||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.Target; | ||
|
||
@Retention(value = RUNTIME) | ||
@Target(ElementType.TYPE) | ||
public @interface GrapeSkip { | ||
String[] vertexDataTypes(); | ||
|
||
String[] edgeDataTypes(); | ||
|
||
String[] msgDataTypes(); | ||
} |
222 changes: 222 additions & 0 deletions
222
...otation/src/main/java/com/alibaba/graphscope/annotation/GrapeSkipAnnotationProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
package com.alibaba.graphscope.annotation; | ||
|
||
import com.alibaba.fastffi.annotation.AnnotationProcessorUtils; | ||
import com.alibaba.graphscope.utils.Unused; | ||
import com.squareup.javapoet.CodeBlock; | ||
import com.squareup.javapoet.JavaFile; | ||
import com.squareup.javapoet.MethodSpec; | ||
import com.squareup.javapoet.ParameterSpec; | ||
import com.squareup.javapoet.TypeSpec; | ||
|
||
import java.io.IOException; | ||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import javax.annotation.processing.AbstractProcessor; | ||
import javax.annotation.processing.Filer; | ||
import javax.annotation.processing.Messager; | ||
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.Element; | ||
import javax.lang.model.element.Modifier; | ||
import javax.lang.model.element.PackageElement; | ||
import javax.lang.model.element.TypeElement; | ||
import javax.lang.model.type.TypeMirror; | ||
import javax.tools.Diagnostic.Kind; | ||
|
||
@SupportedAnnotationTypes("com.alibaba.graphscope.annotation.GrapeSkip") | ||
@SupportedSourceVersion(SourceVersion.RELEASE_8) | ||
public class GrapeSkipAnnotationProcessor extends AbstractProcessor { | ||
|
||
public Messager messager; | ||
|
||
private final Set<String> LEGAL_TYPES = | ||
new HashSet<String>(Arrays.asList("Long", "Integer", "Double", "String", "Empty")); | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* @param annotations | ||
* @param roundEnv | ||
*/ | ||
@Override | ||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { | ||
// if (roundEnv.processingOver()) { | ||
// output(); | ||
// return false; | ||
// } | ||
messager = processingEnv.getMessager(); | ||
for (TypeElement annotation : annotations) { | ||
for (Element e : roundEnv.getElementsAnnotatedWith(annotation)) { | ||
if (e instanceof TypeElement) { | ||
processAnnotation((TypeElement) e, annotation); | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
void processAnnotation(TypeElement typeElement, TypeElement annotation) { | ||
messager.printMessage( | ||
Kind.NOTE, | ||
"visiting element " | ||
+ typeElement.getSimpleName() | ||
+ ", annotation " | ||
+ annotation.getSimpleName(), | ||
typeElement); | ||
if (isSameType(annotation.asType(), GrapeSkip.class)) { | ||
GrapeSkip[] grapeSkips = typeElement.getAnnotationsByType(GrapeSkip.class); | ||
if (grapeSkips.length != 1) { | ||
throw new IllegalStateException("GrapeSkip shall only appear once"); | ||
} | ||
checkAndGenerate(typeElement, grapeSkips[0]); | ||
return; | ||
} | ||
} | ||
|
||
public String checkAndGenerate(TypeElement typeElement, GrapeSkip grapeSkip) { | ||
String dstClassName = "UnusedImpl"; | ||
messager.printMessage( | ||
Kind.NOTE, "Processing element " + typeElement.getSimpleName(), typeElement); | ||
PackageElement packageElement = AnnotationProcessorUtils.getPackageElement(typeElement); | ||
String packageName = packageElement.getQualifiedName().toString(); | ||
messager.printMessage(Kind.NOTE, "package name" + packageName, typeElement); | ||
|
||
TypeSpec.Builder classBuilder = | ||
TypeSpec.classBuilder(dstClassName) | ||
.addModifiers(Modifier.PUBLIC) | ||
.addSuperinterface(Unused.class); | ||
// collect vd,ed,msg types | ||
String[] vdTypes = grapeSkip.vertexDataTypes(); | ||
String[] edTypes = grapeSkip.edgeDataTypes(); | ||
String[] msgTypes = grapeSkip.msgDataTypes(); | ||
check(vdTypes); | ||
check(edTypes); | ||
check(msgTypes); | ||
|
||
// enumerate all possible classes; | ||
int numClasses = msgTypes.length * edTypes.length * vdTypes.length; | ||
String[] classNames = new String[numClasses]; | ||
generateClassNames(numClasses, classNames, vdTypes, edTypes, msgTypes); | ||
messager.printMessage(Kind.NOTE, "generating " + numClasses + " skipClasses", typeElement); | ||
|
||
// filling class first; | ||
fillInSkipClasses(classBuilder, classNames); | ||
messager.printMessage(Kind.NOTE, "Finish filling skip classes", typeElement); | ||
// | ||
// add factory impl | ||
addMethod(classBuilder, classNames, edTypes.length * msgTypes.length, msgTypes.length); | ||
messager.printMessage(Kind.NOTE, "Finish add factory class", typeElement); | ||
|
||
writeTypeSpec(packageName, classBuilder.build(), typeElement); | ||
return dstClassName; | ||
} | ||
|
||
public void generateClassNames( | ||
int numClasses, | ||
String[] classNames, | ||
String[] vdTypes, | ||
String[] edTypes, | ||
String[] msgTypes) { | ||
int ind = 0; | ||
for (String vdType : vdTypes) { | ||
for (String edType : edTypes) { | ||
for (String msgType : msgTypes) { | ||
String cur = vdType + edType + msgType; | ||
classNames[ind] = cur; | ||
ind += 1; | ||
} | ||
} | ||
} | ||
} | ||
|
||
public void fillInSkipClasses(TypeSpec.Builder builder, String[] classNames) { | ||
for (String className : classNames) { | ||
TypeSpec.Builder innerBuilder = | ||
TypeSpec.classBuilder(className) | ||
.addModifiers(Modifier.STATIC, Modifier.PUBLIC) | ||
.addSuperinterface(Unused.class); | ||
builder.addType(innerBuilder.build()); | ||
} | ||
} | ||
|
||
public void addMethod( | ||
TypeSpec.Builder builder, String[] className, int edMultiplyMsg, int msgNum) { | ||
builder.addField(Unused[].class, "skips", Modifier.STATIC, Modifier.PRIVATE); | ||
CodeBlock.Builder staticCodeBuilder = CodeBlock.builder(); | ||
staticCodeBuilder.addStatement( | ||
"skips = new com.alibaba.graphscope.utils.Unused[$L]", className.length); | ||
for (int i = 0; i < className.length; ++i) { | ||
staticCodeBuilder.addStatement("skips[$L] = new $L()", i, className[i]); | ||
} | ||
builder.addStaticBlock(staticCodeBuilder.build()); | ||
|
||
MethodSpec.Builder getUnused = | ||
MethodSpec.methodBuilder("getUnused") | ||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC) | ||
.addParameter(ParameterSpec.builder(Class.class, "vd").build()) | ||
.addParameter(ParameterSpec.builder(Class.class, "ed").build()) | ||
.addParameter(ParameterSpec.builder(Class.class, "msg").build()) | ||
.addCode( | ||
CodeBlock.builder() | ||
.addStatement( | ||
"int a =" | ||
+ " com.alibaba.graphscope.utils.Unused.class2Int(vd)") | ||
.addStatement( | ||
"int b =" | ||
+ " com.alibaba.graphscope.utils.Unused.class2Int(ed)") | ||
.addStatement( | ||
"int c =" | ||
+ " com.alibaba.graphscope.utils.Unused.class2Int(msg)") | ||
.addStatement( | ||
"int ind = a * $L + b * $L + c", | ||
edMultiplyMsg, | ||
msgNum) | ||
.addStatement("return skips[ind]") | ||
.build()) | ||
.returns(Unused.class); | ||
builder.addMethod(getUnused.build()); | ||
} | ||
|
||
private void check(String[] types) { | ||
if (types.length <= 0) { | ||
throw new IllegalStateException("Empty types!"); | ||
} | ||
for (String type : types) { | ||
if (!LEGAL_TYPES.contains(type)) { | ||
throw new IllegalStateException("Unrecognized type " + type); | ||
} | ||
} | ||
} | ||
|
||
private void writeTypeSpec(String packageName, TypeSpec typeSpec, TypeElement typeElement) { | ||
JavaFile javaFile = JavaFile.builder(packageName, typeSpec).build(); | ||
messager.printMessage(Kind.NOTE, javaFile.toString(), typeElement); | ||
try { | ||
Filer filter = processingEnv.getFiler(); | ||
javaFile.writeTo(filter); | ||
} catch (IOException e) { | ||
throw new IllegalStateException( | ||
"Cannot write Java file " | ||
+ javaFile.typeSpec.name | ||
+ ". Please clean the build first.", | ||
e); | ||
} | ||
} | ||
|
||
private boolean isSameType(TypeMirror typeMirror, Class<?> clazz) { | ||
TypeMirror typeMirror2 = getTypeMirror(clazz.getName()); | ||
return processingEnv.getTypeUtils().isSameType(typeMirror, typeMirror2); | ||
} | ||
|
||
private TypeMirror getTypeMirror(String name) { | ||
TypeElement typeElement = processingEnv.getElementUtils().getTypeElement(name); | ||
if (typeElement == null) { | ||
throw new IllegalStateException("Cannot get TypeElement for " + name); | ||
} | ||
return typeElement.asType(); | ||
} | ||
} |
File renamed without changes.
44 changes: 44 additions & 0 deletions
44
...tical_engine/java/grape-annotation/src/main/java/com/alibaba/graphscope/utils/Unused.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.alibaba.graphscope.utils; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
|
||
/** | ||
* A interface which will be subclassed by auto-generated grape skip classes, used to support | ||
* overloading for ffi method. | ||
*/ | ||
public interface Unused { | ||
|
||
static Unused getUnused(Class<?> a, Class<?> b, Class<?> c) { | ||
try { | ||
Class<?> implClass = Class.forName("com.alibaba.graphscope.runtime.UnusedImpl"); | ||
Method method = implClass.getMethod("getUnused", Class.class, Class.class, Class.class); | ||
if (method == null) { | ||
throw new IllegalStateException("fail to get method"); | ||
} | ||
return (Unused) method.invoke(null, a, b, c); | ||
} catch (ClassNotFoundException | ||
| NoSuchMethodException | ||
| InvocationTargetException | ||
| IllegalAccessException e) { | ||
e.printStackTrace(); | ||
} | ||
return null; | ||
} | ||
|
||
static int class2Int(Class clz) { | ||
if (clz.equals(Long.class)) { | ||
return 0; | ||
} else if (clz.equals(Double.class)) { | ||
return 1; | ||
} else if (clz.equals(Integer.class)) { | ||
return 2; | ||
} else if (clz.equals(String.class)) { | ||
return 3; | ||
} else if (clz.getSimpleName().startsWith("EmptyType")) { | ||
return 4; | ||
} else { | ||
throw new IllegalStateException("Not possible"); | ||
} | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
...ape-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
com.alibaba.graphscope.annotation.GrapeSkipAnnotationProcessor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
...engine/java/grape-runtime/src/main/java/com/alibaba/graphscope/runtime/UnusedInvoker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.alibaba.graphscope.runtime; | ||
|
||
import com.alibaba.graphscope.annotation.GrapeSkip; | ||
|
||
@GrapeSkip( | ||
vertexDataTypes = {"Long", "Double", "Integer", "String", "Empty"}, | ||
edgeDataTypes = {"Long", "Double", "Integer", "String", "Empty"}, | ||
msgDataTypes = {"Long", "Double", "Integer", "String", "Empty"}) | ||
public class UnusedInvoker {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters