All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.freedesktop.jaccall.compiletime.FunctorWriter Maven / Gradle / Ivy

The newest version!
package org.freedesktop.jaccall.compiletime;


import org.freedesktop.jaccall.JNI;
import org.freedesktop.jaccall.Pointer;
import org.freedesktop.jaccall.PointerFactory;
import org.freedesktop.jaccall.PointerFunc;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import javax.annotation.Generated;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.LinkedList;

final class FunctorWriter {

    private final Messager messager;
    private final Filer    filer;
    private final Elements elementUtils;

    FunctorWriter(final Messager messager,
                  final Filer filer,
                  final Elements elementUtils) {

        this.messager = messager;
        this.filer = filer;
        this.elementUtils = elementUtils;
    }

    public void process(final TypeElement typeElement) {
        for (final ExecutableElement executableElement : ElementFilter.methodsIn(typeElement.getEnclosedElements())) {
            writeFunctorImplementation(typeElement,
                                       executableElement);
        }
    }

    private void writeFunctorImplementation(final TypeElement typeElement,
                                            final ExecutableElement executableElement) {

        final String factoryName        = "Pointer" + typeElement.getSimpleName();
        final String cFunctorName       = typeElement.getSimpleName() + "_Jaccall_C";
        final String javaFunctorName    = typeElement.getSimpleName() + "_Jaccall_J";
        final String pointerFactoryName = typeElement.getSimpleName() + "_PointerFactory";


        writeFactory(factoryName,
                     javaFunctorName,
                     typeElement,
                     executableElement);
        writeCFunctor(factoryName,
                      cFunctorName,
                      typeElement,
                      executableElement);
        writeJavaFunctor(factoryName,
                         javaFunctorName,
                         typeElement,
                         executableElement);
        writePointerFactory(factoryName,
                            pointerFactoryName,
                            cFunctorName,
                            typeElement);
    }

    private void writePointerFactory(final String factoryName,
                                     final String pointerFactoryName,
                                     final String cFunctorName,
                                     final TypeElement element) {

        final AnnotationSpec annotationSpec = AnnotationSpec.builder(Generated.class)
                                                            .addMember("value",
                                                                       "$S",
                                                                       JaccallGenerator.class.getName())
                                                            .build();

        final String packageName = this.elementUtils.getPackageOf(element)
                                                    .toString();

        final MethodSpec createFunc = MethodSpec.methodBuilder("create")
                                                .addAnnotation(Override.class)
                                                .addModifiers(Modifier.PUBLIC)
                                                .returns(ClassName.get(packageName,
                                                                       factoryName))
                                                .addParameter(Type.class,
                                                              "type")
                                                .addParameter(long.class,
                                                              "address")
                                                .addParameter(boolean.class,
                                                              "autoFree")
                                                .addStatement("return new $T(address)",
                                                              ClassName.get(packageName,
                                                                            cFunctorName))
                                                .build();

        final TypeSpec typeSpec = TypeSpec.classBuilder(pointerFactoryName)
                                          .addAnnotation(annotationSpec)
                                          .addModifiers(Modifier.PUBLIC,
                                                        Modifier.FINAL)
                                          .addSuperinterface(ParameterizedTypeName.get(ClassName.get(PointerFactory.class),
                                                                                       ClassName.get(packageName,
                                                                                                     factoryName)))
                                          .addMethod(createFunc)
                                          .build();

        final JavaFile javaFile = JavaFile.builder(packageName,
                                                   typeSpec)
                                          .skipJavaLangImports(true)
                                          .build();
        try {
            javaFile.writeTo(this.filer);
        }
        catch (final IOException e) {
            this.messager.printMessage(Diagnostic.Kind.ERROR,
                                       "Could not write linksymbols source file: \n" + javaFile.toString(),
                                       element);
            e.printStackTrace();
        }
    }

    private void writeJavaFunctor(final String factoryName,
                                  final String javaFunctorName,
                                  final TypeElement element,
                                  final ExecutableElement executableElement) {

        final AnnotationSpec annotationSpec = AnnotationSpec.builder(Generated.class)
                                                            .addMember("value",
                                                                       "$S",
                                                                       JaccallGenerator.class.getName())
                                                            .build();

        final String packageName = this.elementUtils.getPackageOf(element)
                                                    .toString();

        final TypeMirror                returnType      = executableElement.getReturnType();
        final LinkedList $ParameterSpecs = new LinkedList<>();
        final StringBuilder             parameterNames  = new StringBuilder();

        final java.util.List parameters = executableElement.getParameters();
        for (int i = 0; i < parameters.size(); i++) {
            final VariableElement variableElement = parameters.get(i);
            final String parameterName = variableElement.getSimpleName()
                                                        .toString();
            $ParameterSpecs.add(ParameterSpec.builder(ClassName.get(variableElement.asType()),
                                                      parameterName)
                                             .build());
            if (i != 0) {
                parameterNames.append(", ");
            }
            parameterNames.append(parameterName);
        }

        final CodeBlock $Statement = CodeBlock.builder()
                                              .add(returnType.getKind()
                                                             .equals(TypeKind.VOID) ? "" : "return ")
                                              .add("this.function.$$($L)",
                                                   parameterNames)
                                              .build();
        final MethodSpec $ = MethodSpec.methodBuilder("$")
                                       .addModifiers(Modifier.PUBLIC)
                                       .addAnnotation(Override.class)
                                       .returns(ClassName.get(returnType))
                                       .addParameters($ParameterSpecs)
                                       .addStatement("$L",
                                                     $Statement)
                                       .build();

        final MethodSpec constructor = MethodSpec.constructorBuilder()
                                                 .addParameter(ClassName.get(element),
                                                               "function")
                                                 .addStatement("super($T.ffi_closure(FFI_CIF, function, JNI_METHOD_ID))",
                                                               JNI.class)
                                                 .addStatement("this.function = function")
                                                 .build();

        final FieldSpec jniMethodId = FieldSpec.builder(long.class,
                                                        "JNI_METHOD_ID",
                                                        Modifier.PRIVATE,
                                                        Modifier.STATIC,
                                                        Modifier.FINAL)
                                               .initializer("$T.GetMethodID($T.class, $S, $S)",
                                                            JNI.class,
                                                            ClassName.get(element),
                                                            "$",
                                                            new MethodParser(this.messager).parseJniSignature(executableElement))
                                               .build();

        final TypeSpec typeSpec = TypeSpec.classBuilder(javaFunctorName)
                                          .addAnnotation(annotationSpec)
                                          .addModifiers(Modifier.FINAL)
                                          .superclass(ClassName.get(packageName,
                                                                    factoryName))
                                          .addField(jniMethodId)
                                          .addField(ClassName.get(element),
                                                    "function",
                                                    Modifier.PRIVATE,
                                                    Modifier.FINAL)
                                          .addMethod(constructor)
                                          .addMethod($)
                                          .build();

        final JavaFile javaFile = JavaFile.builder(packageName,
                                                   typeSpec)
                                          .skipJavaLangImports(true)
                                          .build();
        try {
            javaFile.writeTo(this.filer);
        }
        catch (final IOException e) {
            this.messager.printMessage(Diagnostic.Kind.ERROR,
                                       "Could not write linksymbols source file: \n" + javaFile.toString(),
                                       element);
            e.printStackTrace();
        }
    }

    private void writeCFunctor(final String factoryName,
                               final String cFunctorName,
                               final TypeElement element,
                               final ExecutableElement executableElement) {

        final AnnotationSpec annotationSpec = AnnotationSpec.builder(Generated.class)
                                                            .addMember("value",
                                                                       "$S",
                                                                       JaccallGenerator.class.getName())
                                                            .build();

        final String packageName = this.elementUtils.getPackageOf(element)
                                                    .toString();

        final MethodSpec constructor = MethodSpec.constructorBuilder()
                                                 .addParameter(long.class,
                                                               "address")
                                                 .addStatement("super(address)")
                                                 .build();

        final TypeMirror                returnType      = executableElement.getReturnType();
        final LinkedList $ParameterSpecs = new LinkedList<>();
        final StringBuilder             parameterNames  = new StringBuilder();

        final java.util.List parameters = executableElement.getParameters();
        for (int i = 0; i < parameters.size(); i++) {
            final VariableElement variableElement = parameters.get(i);
            final String parameterName = variableElement.getSimpleName()
                                                        .toString();
            $ParameterSpecs.add(ParameterSpec.builder(ClassName.get(variableElement.asType()),
                                                      parameterName)
                                             .build());
            if (i != 0) {
                parameterNames.append(", ");
            }
            parameterNames.append(parameterName);
        }


        final CodeBlock $Statement = CodeBlock.builder()
                                              .add(returnType.getKind()
                                                             .equals(TypeKind.VOID) ? "" : "return ")
                                              .add("_$$(this.address, $L)",
                                                   parameterNames)
                                              .build();
        final MethodSpec $ = MethodSpec.methodBuilder("$")
                                       .addModifiers(Modifier.PUBLIC)
                                       .addAnnotation(Override.class)
                                       .returns(ClassName.get(returnType))
                                       .addParameters($ParameterSpecs)
                                       .addStatement("$L",
                                                     $Statement)
                                       .build();

        final LinkedList _$ParameterSpecs = new LinkedList<>($ParameterSpecs);
        _$ParameterSpecs.addFirst(ParameterSpec.builder(long.class,
                                                        "address")
                                               .build());
        final MethodSpec _$ = MethodSpec.methodBuilder("_$")
                                        .addModifiers(Modifier.PRIVATE,
                                                      Modifier.STATIC,
                                                      Modifier.NATIVE)
                                        .returns(ClassName.get(returnType))
                                        .addParameters(_$ParameterSpecs)
                                        .build();

        final CodeBlock staticBlock = CodeBlock.builder()
                                               .addStatement("$T.linkFuncPtr($T.class,  $S, $L, $S, FFI_CIF)",
                                                             JNI.class,
                                                             ClassName.get(packageName,
                                                                           cFunctorName),
                                                             "_$",
                                                             parameters.size() + 1,
                                                             new MethodParser(this.messager).parseJniSignature(executableElement)
                                                                                            .replace("(",
                                                                                                     "(J"))
                                               .build();

        final TypeSpec typeSpec = TypeSpec.classBuilder(cFunctorName)
                                          .addAnnotation(annotationSpec)
                                          .addModifiers(Modifier.FINAL)
                                          .addStaticBlock(staticBlock)
                                          .superclass(ClassName.get(packageName,
                                                                    factoryName))
                                          .addMethod(constructor)
                                          .addMethod($)
                                          .addMethod(_$)
                                          .build();

        final JavaFile javaFile = JavaFile.builder(packageName,
                                                   typeSpec)
                                          .skipJavaLangImports(true)
                                          .build();
        try {
            javaFile.writeTo(this.filer);
        }
        catch (final IOException e) {
            this.messager.printMessage(Diagnostic.Kind.ERROR,
                                       "Could not write linksymbols source file: \n" + javaFile.toString(),
                                       element);
            e.printStackTrace();
        }
    }

    private void writeFactory(final String factoryName,
                              final String javaFunctorName,
                              final TypeElement element,
                              final ExecutableElement executableElement) {

        final AnnotationSpec annotationSpec = AnnotationSpec.builder(Generated.class)
                                                            .addMember("value",
                                                                       "$S",
                                                                       JaccallGenerator.class.getName())
                                                            .build();

        final String packageName = this.elementUtils.getPackageOf(element)
                                                    .toString();

        final FieldSpec ffiCif = FieldSpec.builder(long.class,
                                                   "FFI_CIF",
                                                   Modifier.STATIC,
                                                   Modifier.FINAL)
                                          .initializer("$T.ffi_callInterface($L)",
                                                       JNI.class,
                                                       new MethodParser(this.messager).parseFfiSignature(executableElement))
                                          .build();

        final MethodSpec constructor = MethodSpec.constructorBuilder()
                                                 .addParameter(long.class,
                                                               "address")
                                                 .addStatement("super($T.class, address)",
                                                               ClassName.get(element))
                                                 .build();

        final MethodSpec nref = MethodSpec.methodBuilder("nref")
                                          .addModifiers(Modifier.PUBLIC,
                                                        Modifier.STATIC)
                                          .returns(ParameterizedTypeName.get(ClassName.get(Pointer.class),
                                                                             ClassName.get(element)))
                                          .addParameter(ClassName.get(element),
                                                        "function")
                                          .beginControlFlow("if(function instanceof $T)",
                                                            ClassName.get(packageName,
                                                                          factoryName))
                                          .addStatement("return ($T)function",
                                                        ClassName.get(packageName,
                                                                      factoryName))
                                          .endControlFlow()
                                          .addStatement("return new $T(function)",
                                                        ClassName.get(packageName,
                                                                      javaFunctorName))
                                          .build();

        final TypeSpec typeSpec = TypeSpec.classBuilder(factoryName)
                                          .addAnnotation(annotationSpec)
                                          .addModifiers(Modifier.PUBLIC,
                                                        Modifier.ABSTRACT)
                                          .superclass(ParameterizedTypeName.get(ClassName.get(PointerFunc.class),
                                                                                ClassName.get(element)))
                                          .addSuperinterface(TypeName.get(element.asType()))
                                          .addField(ffiCif)
                                          .addMethod(constructor)
                                          .addMethod(nref)
                                          .build();

        final JavaFile javaFile = JavaFile.builder(packageName,
                                                   typeSpec)
                                          .skipJavaLangImports(true)
                                          .build();
        try {
            javaFile.writeTo(this.filer);
        }
        catch (final IOException e) {
            this.messager.printMessage(Diagnostic.Kind.ERROR,
                                       "Could not write linksymbols source file: \n" + javaFile.toString(),
                                       element);
            e.printStackTrace();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy