< prev index next >

src/jdk.jextract/share/classes/com/sun/tools/jextract/AsmCodeFactory.java

Print this page

        

@@ -21,10 +21,14 @@
  * questions.
  */
 package com.sun.tools.jextract;
 
 import java.io.IOException;
+import java.foreign.Libraries;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Method;
 import java.foreign.layout.Layout;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collections;

@@ -37,10 +41,11 @@
 import java.util.stream.Stream;
 import jdk.internal.clang.SourceLocation;
 import jdk.internal.clang.Type;
 import jdk.internal.clang.TypeKind;
 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
 import jdk.internal.org.objectweb.asm.ClassVisitor;
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.internal.org.objectweb.asm.TypeReference;
 import com.sun.tools.jextract.tree.EnumTree;

@@ -56,18 +61,26 @@
 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ABSTRACT;
 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ANNOTATION;
 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_INTERFACE;
 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS;
 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
+import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
 import static jdk.internal.org.objectweb.asm.Opcodes.DRETURN;
 import static jdk.internal.org.objectweb.asm.Opcodes.FRETURN;
+import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC;
 import static jdk.internal.org.objectweb.asm.Opcodes.I2C;
+import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
+import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE;
+import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
 import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
 import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN;
+import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
 import static jdk.internal.org.objectweb.asm.Opcodes.V1_8;
 
 /**
  * Scan a header file and generate classes for entities defined in that header
  * file.

@@ -78,38 +91,108 @@
     private static final String NATIVE_HEADER = ANNOTATION_PKG_PREFIX + "NativeHeader;";
     private static final String NATIVE_LOCATION = ANNOTATION_PKG_PREFIX + "NativeLocation;";
     private static final String NATIVE_STRUCT = ANNOTATION_PKG_PREFIX + "NativeStruct;";
 
     private final Context ctx;
+    // static methods forwards for this header file
+    private final ClassWriter statics_cw;
     private final ClassWriter global_cw;
     // to avoid duplicate generation of methods, field accessors, macros
     private final Set<String> global_methods = new HashSet<>();
     private final Set<String> global_fields = new HashSet<>();
     private final Set<String> global_macros = new HashSet<>();
     private final String internal_name;
+    private final String internal_name_desc;
     private final HeaderFile owner;
     private final Map<String, byte[]> types;
     private final Logger logger = Logger.getLogger(getClass().getPackage().getName());
     private final List<String> headerDeclarations = new ArrayList<>();
     private transient boolean built = false;
 
+    // constants used by static forwarder class
+    private static final String STATICS_CLASS_NAME_SUFFIX = "_h";
+    private static final String STATICS_LIBRARY_FIELD_NAME = "_theLibrary";
+
     AsmCodeFactory(Context ctx, HeaderFile header) {
         this.ctx = ctx;
         logger.info(() -> "Instantiate AsmCodeFactory for " + header.path);
         this.owner = header;
         this.internal_name = Utils.toInternalName(owner.pkgName, owner.clsName);
+        this.internal_name_desc = "L" + internal_name + ";";
         this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
         this.types = new HashMap<>();
         global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
                 internal_name,
                 null, "java/lang/Object", null);
+        // if there is no -l option specified, we cannot do Libraries.bind(Lookup, Class).
+        // so, don't generate statics forwarder.
+        if (librariesSpecified()) {
+            this.statics_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+            this.statics_cw.visit(V1_8, ACC_PUBLIC | ACC_FINAL,
+                internal_name + STATICS_CLASS_NAME_SUFFIX,
+                null, "java/lang/Object", null);
+            staticsInitializer();
+        } else {
+            this.statics_cw = null;
+        }
+    }
+
+    // emit library interface static field and <clinit> initializer for that field
+    private void staticsInitializer() {
+        // library interface field
+        FieldVisitor fv = statics_cw.visitField(ACC_PRIVATE|ACC_STATIC|ACC_FINAL,
+            STATICS_LIBRARY_FIELD_NAME, internal_name_desc, null, null);
+        fv.visitEnd();
+
+        // <clinit> to bind library interface field
+        MethodVisitor mv = statics_cw.visitMethod(ACC_STATIC,
+            "<clinit>", "()V", null, null);
+        mv.visitCode();
+
+        // MethodHandles.lookup()
+        Method lookupMethod = null;
+        try {
+            lookupMethod = MethodHandles.class.getMethod("lookup");
+        } catch (NoSuchMethodException nsme) {
+            throw new RuntimeException(nsme);
+        }
+        mv.visitMethodInsn(INVOKESTATIC,
+            jdk.internal.org.objectweb.asm.Type.getInternalName(MethodHandles.class), "lookup",
+            jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(lookupMethod), false);
+
+        // ldc library-interface-class
+        mv.visitLdcInsn(jdk.internal.org.objectweb.asm.Type.getObjectType(internal_name));
+
+        // Libraries.bind(lookup, class);
+        Method bindMethod = null;
+        try {
+            bindMethod = Libraries.class.getMethod("bind", Lookup.class, Class.class);
+        } catch (NoSuchMethodException nsme) {
+            throw new RuntimeException(nsme);
+        }
+        mv.visitMethodInsn(INVOKESTATIC,
+            jdk.internal.org.objectweb.asm.Type.getInternalName(Libraries.class), "bind",
+            jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(bindMethod), false);
+
+        // store it in library interface field
+        mv.visitTypeInsn(CHECKCAST, internal_name);
+        mv.visitFieldInsn(PUTSTATIC, internal_name + STATICS_CLASS_NAME_SUFFIX,
+            STATICS_LIBRARY_FIELD_NAME, internal_name_desc);
+
+        mv.visitInsn(RETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+    }
+
+    private boolean librariesSpecified() {
+        return owner.libraries != null && !owner.libraries.isEmpty();
     }
 
     private void generateNativeHeader() {
         AnnotationVisitor av = global_cw.visitAnnotation(NATIVE_HEADER, true);
         av.visit("path", owner.path.toAbsolutePath().toString());
-        if (owner.libraries != null && !owner.libraries.isEmpty()) {
+        if (librariesSpecified()) {
             AnnotationVisitor libNames = av.visitArray("libraries");
             for (String name : owner.libraries) {
                 libNames.visit(null, name);
             }
             libNames.visitEnd();

@@ -235,10 +318,12 @@
             default:
                 throw new AssertionError("should not reach here");
         }
         mv.visitMaxs(1, 1);
         mv.visitEnd();
+        // static forwarder method for this enum constant
+        emitStaticsForwarder(name, "()" + desc, null);
     }
 
     @Override
     public Void visitStruct(StructTree structTree, JType jt) {
         String nativeName = structTree.name();

@@ -478,13 +563,43 @@
                         null, alias.getAnnotationDescriptor(), true)
                   .visitEnd();
             }
         }
         mv.visitEnd();
+        emitStaticsForwarder(funcTree.name(), fn.getDescriptor(), fn.getSignature());
         return null;
     }
 
+    // emit static forwarder method for a specific library interface method
+    private void emitStaticsForwarder(String name, String desc, String signature) {
+        if (statics_cw == null) {
+            return;
+        }
+        MethodVisitor statics_mv = statics_cw.visitMethod(ACC_PUBLIC | ACC_STATIC,
+            name, desc, signature, null);
+        statics_mv.visitCode();
+
+        // load library interface (static) field
+        statics_mv.visitFieldInsn(GETSTATIC, internal_name + STATICS_CLASS_NAME_SUFFIX,
+            STATICS_LIBRARY_FIELD_NAME, internal_name_desc);
+
+        // forward the call to the interface
+        jdk.internal.org.objectweb.asm.Type[] argTypes = jdk.internal.org.objectweb.asm.Type.getArgumentTypes(desc);
+        jdk.internal.org.objectweb.asm.Type retType = jdk.internal.org.objectweb.asm.Type.getReturnType(desc);
+
+        int loadIdx = 0;
+        for (int i = 0; i < argTypes.length; i++) {
+            statics_mv.visitVarInsn(argTypes[i].getOpcode(ILOAD), loadIdx);
+            loadIdx += argTypes[i].getSize();
+        }
+        statics_mv.visitMethodInsn(INVOKEINTERFACE, internal_name, name, desc, true);
+        statics_mv.visitInsn(retType.getOpcode(IRETURN));
+
+        statics_mv.visitMaxs(0, 0);
+        statics_mv.visitEnd();
+    }
+
     protected AsmCodeFactory addType(JType jt, Tree tree) {
         JType2 jt2 = null;
         if (jt instanceof JType2) {
             jt2 = (JType2) jt;
             jt = jt2.getDelegate();

@@ -564,10 +679,12 @@
         } else if (macroType.equals(String.class)) {
             mv.visitInsn(ARETURN);
         }
         mv.visitMaxs(0, 0);
         mv.visitEnd();
+        // static forwarder method for this macro
+        emitStaticsForwarder(name, sig, sig);
         return null;
     }
 
     protected synchronized void produce() {
         if (built) {

@@ -575,10 +692,13 @@
         }
         built = true;
         generateNativeHeader();
         try {
             writeClassFile(global_cw, owner.clsName);
+            if (statics_cw != null) {
+                writeClassFile(statics_cw, owner.clsName + STATICS_CLASS_NAME_SUFFIX);
+            }
         } catch (IOException ex) {
             handleException(ex);
         }
     }
 
< prev index next >