aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/glavo/javah/JNIGenerator.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/glavo/javah/JNIGenerator.java')
-rw-r--r--src/main/java/org/glavo/javah/JNIGenerator.java141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/main/java/org/glavo/javah/JNIGenerator.java b/src/main/java/org/glavo/javah/JNIGenerator.java
new file mode 100644
index 0000000..a00bc6c
--- /dev/null
+++ b/src/main/java/org/glavo/javah/JNIGenerator.java
@@ -0,0 +1,141 @@
+package org.glavo.javah;
+
+import org.objectweb.asm.*;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class JNIGenerator extends ClassVisitor {
+ public static final String FILE_HEADER =
+ "/* DO NOT EDIT THIS FILE - it is machine generated */\n" +
+ "#include <jni.h>\n";
+
+ public static final String FILE_END = "\n";
+
+ private final PrintWriter output;
+ private final Path classFile;
+
+ private String className;
+
+ private Map<String, Integer> methodNameCount = new HashMap<>();
+ private LinkedList<NativeMethod> methods = new LinkedList<>();
+ private LinkedList<ConstantField> constants = new LinkedList<>();
+
+ public JNIGenerator(PrintWriter output, Path classFile) {
+ super(Opcodes.ASM7);
+ Objects.requireNonNull(output);
+ Objects.requireNonNull(classFile);
+ this.output = output;
+ this.classFile = classFile;
+ }
+
+ public void generate() {
+ ClassReader cls = null;
+ try {
+ cls = new ClassReader(Files.readAllBytes(classFile));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ cls.accept(this, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+
+ String name = Utils.encode(className).replace('/', '_');
+ String guard = "_Included_" + className;
+
+ output.println("/* Header for class " + className + " */\n\n");
+ output.println("#ifndef " + guard);
+ output.println("#define " + guard);
+ output.println("#ifdef __cplusplus");
+ output.println("extern \"C\" {");
+ output.println("#endif");
+
+ for (ConstantField constant : constants) {
+ String cm = name + "_" + Utils.encode(constant.name);
+ String value;
+ if (constant.value instanceof Float) {
+ value = constant.value.toString() + "f";
+ } else if (constant.value instanceof Long) {
+ value = constant.value.toString() + "i64";
+ } else {
+ value = constant.value.toString();
+ }
+ output.println("#undef " + cm);
+ output.println("#define " + cm + " " + value);
+ }
+ for (NativeMethod method : methods) {
+ String mm = "Java_" + name + "_" + Utils.encode(method.name);
+ if (methodNameCount.getOrDefault(method.name, 1) > 1) {
+ mm = mm + "__" + Utils.mangledArgSignature(method.type);
+ }
+ String r = Utils.mapToNativeType(method.type.getReturnType());
+ String args = Stream.concat(Stream.of("JNIEnv *", method.isStatic ? "jclass" : "jobject"),
+ Arrays.stream(method.type.getArgumentTypes()).map(Utils::mapToNativeType))
+ .collect(Collectors.joining(","));
+
+ output.println("/*");
+ output.println(" * Class: " + name);
+ output.println(" * Method: " + mm);
+ output.println(" * Signature: " + method.type);
+ output.println(" */");
+ output.println("JNIEXPORT " + r + " JNICALL " + mm);
+ output.println(" (" + args + ");");
+ output.println();
+ }
+
+ output.println("#ifdef __cplusplus");
+ output.println("}");
+ output.println("#endif");
+ output.println("#endif");
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ className = name;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
+ if ((access & Opcodes.ACC_NATIVE) != 0) {
+ methodNameCount.put(name, methodNameCount.getOrDefault(name, 0) + 1);
+ methods.add(new NativeMethod(name, Type.getType(descriptor), (access & Opcodes.ACC_STATIC) != 0));
+ }
+ return null;
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
+ if (value != null && !(value instanceof String)) {
+ constants.add(new ConstantField(name, Type.getType(descriptor), value));
+ }
+ return null;
+ }
+
+ private static class NativeMethod {
+ String name;
+ Type type;
+ boolean isStatic;
+
+ public NativeMethod(String name, Type type, boolean isStatic) {
+ this.name = name;
+ this.type = type;
+ this.isStatic = isStatic;
+ }
+ }
+
+ private static class ConstantField {
+ String name;
+ Type type;
+ Object value;
+
+ public ConstantField(String name, Type type, Object value) {
+ this.name = name;
+ this.type = type;
+ this.value = value;
+ }
+ }
+}