aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/glavo/javah/HeaderGenerator.java251
1 files changed, 218 insertions, 33 deletions
diff --git a/src/main/java/org/glavo/javah/HeaderGenerator.java b/src/main/java/org/glavo/javah/HeaderGenerator.java
index 31eeef6..81fe6c5 100644
--- a/src/main/java/org/glavo/javah/HeaderGenerator.java
+++ b/src/main/java/org/glavo/javah/HeaderGenerator.java
@@ -3,14 +3,25 @@ package org.glavo.javah;
import org.objectweb.asm.*;
import java.io.*;
-import java.nio.file.Files;
-import java.nio.file.Path;
+import java.nio.file.*;
import java.util.*;
public final class HeaderGenerator {
private static final Path[] EMPTY_PATH_ARRAY = new Path[0];
private static final List<String> THROWABLE_NAME_LIST = Arrays.asList("Ljava/lang/Throwable;", "Ljava/lang/Error;", "Ljava/lang/Exception");
private static final HeaderGenerator generator = new HeaderGenerator();
+ public static final String VERSION = "0.1.1";
+ private static final String message = "Usage: \n" +
+ " javah [options] <classes>\n" +
+ "where [options] include:\n" +
+ " -o <file> Output file (only one of -d or -o may be used)\n" +
+ " -d <dir> Output directory\n" +
+ " -h -help --help -? Print this message\n" +
+ " -version Print version information\n" +
+ " -classpath <path> Path from which to load classes\n" +
+ " -cp <path> Path from which to load classes\n" +
+ "<classes> are specified with their fully qualified names\n" +
+ "(for example, java.lang.Object).";
private static String escape(String source) {
StringBuilder builder = new StringBuilder();
@@ -106,28 +117,7 @@ public final class HeaderGenerator {
return true;
}
try {
- ClassReader reader = null;
- loop:
- for (Path path : classPaths) {
- if (!Files.exists(path)) {
- continue;
- }
- String[] ps = (className + ".class").split("/");
- for (String p : ps) {
- path = path.resolve(p);
- if (!Files.exists(path)) {
- continue loop;
- }
- }
- try {
- reader = new ClassReader(Files.newInputStream(path));
- break;
- } catch (IOException ignored) {
- }
- }
- if (reader == null && useRuntimeClassPath) {
- reader = new ClassReader(className.replace('/', '.'));
- }
+ ClassReader reader = findClass(className);
if (reader == null) {
return false;
}
@@ -180,6 +170,36 @@ public final class HeaderGenerator {
}
}
+ private ClassReader findClass(String name) {
+ loop:
+ for (Path path : classPaths) {
+ path = path.toAbsolutePath();
+ if (!Files.exists(path)) {
+ continue;
+ }
+ String[] ps = name.split("/|\\.");
+ if (ps.length == 0) {
+ continue;
+ }
+ ps[ps.length - 1] += ".class";
+ for (String p : ps) {
+ path = path.resolve(p);
+ if (!Files.exists(path)) {
+ continue loop;
+ }
+ }
+ try {
+ return new ClassReader(Files.newInputStream(path));
+ } catch (IOException ignored) {
+ }
+ }
+ try {
+ return new ClassReader(name.replace('/', '.'));
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
private void classGenerateFunctionDeclarations(Generator generator, PrintWriter output) {
String className = escape(generator.getClassName());
@@ -230,13 +250,7 @@ public final class HeaderGenerator {
classGenerateFunctionDeclarations(generator, output);
}
- public void classGenerateHeader(ClassReader reader, PrintWriter output) {
- output.println("/* DO NOT EDIT THIS FILE - it is machine generated */");
- output.println("#include <jni.h>");
-
- Generator generator = new Generator();
- reader.accept(generator, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
-
+ private void classGenerateHeaderWithoutInclude(Generator generator, PrintWriter output) {
StringBuilder builder = new StringBuilder();
String className = escape(generator.getClassName());
@@ -256,7 +270,25 @@ public final class HeaderGenerator {
"}\n" +
"#endif\n" +
"#endif\n");
+ }
+
+ private void classGenerateHeaderWithoutInclude(ClassReader reader, PrintWriter output) {
+ Generator generator = new Generator();
+ reader.accept(generator, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ classGenerateHeaderWithoutInclude(generator, output);
+ }
+
+ private void classGenerateHeader(Generator generator, PrintWriter output) {
+ output.println("/* DO NOT EDIT THIS FILE - it is machine generated */");
+ output.println("#include <jni.h>");
+ classGenerateHeaderWithoutInclude(generator, output);
+ }
+
+ public void classGenerateHeader(ClassReader reader, PrintWriter output) {
+ Generator generator = new Generator();
+ reader.accept(generator, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ classGenerateHeader(generator, output);
}
public void classGenerateHeader(byte[] classFile, PrintWriter output) {
@@ -268,7 +300,11 @@ public final class HeaderGenerator {
}
public void classGenerateHeader(String className, PrintWriter output) throws IOException {
- classGenerateHeader(new ClassReader(className), output);
+ ClassReader reader = findClass(className);
+ if (reader == null) {
+ throw new IOException();
+ }
+ classGenerateHeader(reader, output);
}
public void classGenerateHeader(InputStream input, PrintWriter output) throws IOException {
@@ -293,6 +329,154 @@ public final class HeaderGenerator {
}
public static void main(String[] args) throws IOException {
+ boolean isWindows = System.getProperty("os.name", "").toLowerCase().contains("windows");
+ ArrayList<Path> cps = new ArrayList<>();
+ Path outputFile = null;
+ Path outputDir = null;
+ ArrayList<String> classNames = new ArrayList<>();
+
+ if (args.length == 0) {
+ System.out.println(message);
+ return;
+ }
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if (arg.startsWith("-")) {
+ switch (arg) {
+ case "-h":
+ case "-help":
+ case "--help":
+ case "-?":
+ System.out.println(message);
+ return;
+ case "-version":
+ System.out.println();
+ return;
+ case "-cp":
+ case "-classpath":
+ i++;
+ if (i == args.length) {
+ System.err.println("javah: " + arg + " requires an argument.");
+ return;
+ }
+ String[] s = args[i].split(isWindows ? ";" : ":");
+ for (String ss : s) {
+ Path p = Paths.get(ss);
+ if (Files.exists(p)) {
+ if (Files.isDirectory(p)) {
+ cps.add(p);
+ } else if (ss.toLowerCase().endsWith(".jar") || ss.toLowerCase().endsWith(".zip")) {
+ try {
+ cps.add(FileSystems.newFileSystem(p, null).getPath("/"));
+ } catch (Exception ignored) {
+ }
+ }
+ }
+ }
+ break;
+ case "-o":
+ i++;
+ if (i == args.length) {
+ System.err.println("javah: " + arg + " requires an argument.");
+ return;
+ }
+ if (outputDir != null) {
+ System.err.println("Error: Can't mix options -d and -o. Try -help.");
+ return;
+ }
+ outputFile = Paths.get(args[i]);
+ break;
+
+ case "-d":
+ i++;
+ if (i == args.length) {
+ System.err.println("javah: " + arg + " requires an argument.");
+ return;
+ }
+ if (outputFile != null) {
+ System.err.println("Error: Can't mix options -d and -o. Try -help.");
+ return;
+ }
+ outputDir = Paths.get(args[i]);
+
+ break;
+ default:
+ System.err.println("Error: unknown option: " + arg);
+ return;
+ }
+
+ } else {
+ if (arg.contains("/")) {
+ int idx = arg.indexOf('/');
+ if (idx != arg.lastIndexOf('/')) {
+ System.err.println("Not a valid class name: " + arg);
+ }
+ arg = arg.substring(idx + 1);
+ }
+ classNames.add(arg);
+ }
+
+ if (outputDir == null && outputFile == null) {
+ outputDir = Paths.get(System.getProperty("user.dir", "."));
+ }
+ if (cps.isEmpty()) {
+ cps.add(Paths.get(System.getProperty("user.dir", ".")));
+ }
+
+ if (outputDir != null && Files.notExists(outputDir)) {
+ Files.createDirectories(outputDir);
+ }
+
+ if (outputFile != null && Files.notExists(outputFile)) {
+ if (Files.notExists(outputFile.getParent())) {
+ Files.createDirectories(outputFile.getParent());
+ }
+ }
+
+ HeaderGenerator generator = new HeaderGenerator(cps.toArray(new Path[0]));
+ if (outputFile != null) {
+ if (Files.notExists(outputFile.getParent())) {
+ Files.createDirectories(outputFile.getParent());
+ }
+ try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(outputFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
+ boolean first = true;
+ for (String name : classNames) {
+ ClassReader reader = generator.findClass(name);
+ if (reader == null) {
+ System.err.println("Error: Could not find class file for '" + name + "'.");
+ return;
+ }
+ Generator g = new Generator();
+ reader.accept(g, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ if (first) {
+ generator.classGenerateHeader(g, writer);
+ first = false;
+ } else {
+ generator.classGenerateHeaderWithoutInclude(g, writer);
+ }
+ }
+ }
+ }
+ if (outputDir != null) {
+ if (Files.notExists(outputDir)) {
+ Files.createDirectories(outputDir);
+ }
+ for (String name : classNames) {
+ ClassReader reader = generator.findClass(name);
+ if (reader == null) {
+ System.err.println("Error: Could not find class file for '" + name + "'.");
+ return;
+ }
+ Generator g = new Generator();
+ reader.accept(g, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+
+ try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(outputDir.resolve(name.replace('.', '_') + ".h"), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
+ generator.classGenerateHeader(g, writer);
+ }
+ }
+ }
+
+ }
}
}
@@ -309,6 +493,7 @@ class MethodDesc {
class Generator extends ClassVisitor {
private String className;
+ private Map<String, String> constants = new LinkedHashMap<>();
private Map<String, Set<MethodDesc>> methods = new LinkedHashMap<String, Set<MethodDesc>>();
public Generator() {
@@ -326,7 +511,7 @@ class Generator extends ClassVisitor {
if (methods.containsKey(name)) {
methods.get(name).add(new MethodDesc((access & Opcodes.ACC_STATIC) != 0, descriptor));
} else {
- LinkedHashSet<MethodDesc> set = new LinkedHashSet<MethodDesc>();
+ LinkedHashSet<MethodDesc> set = new LinkedHashSet<>();
set.add(new MethodDesc((access & Opcodes.ACC_STATIC) != 0, descriptor));
methods.put(name, set);
}