7150368: javac should include basic ability to generate native headers
Reviewed-by: mcimadamore, darcy, ohrstrom
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java Fri Mar 09 17:10:56 2012 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java Tue Mar 13 15:43:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -126,6 +126,7 @@
public final Type cloneableType;
public final Type serializableType;
public final Type methodHandleType;
+ public final Type nativeHeaderType;
public final Type polymorphicSignatureType;
public final Type throwableType;
public final Type errorType;
@@ -477,6 +478,7 @@
List.of(exceptionType), methodClass),
autoCloseableType.tsym);
trustMeType = enterClass("java.lang.SafeVarargs");
+ nativeHeaderType = enterClass("javax.tools.annotation.GenerateNativeHeader");
synthesizeEmptyInterfaceIfMissing(autoCloseableType);
synthesizeEmptyInterfaceIfMissing(cloneableType);
--- a/langtools/src/share/classes/com/sun/tools/javac/file/Locations.java Fri Mar 09 17:10:56 2012 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/file/Locations.java Tue Mar 13 15:43:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -648,7 +648,8 @@
new SimpleLocationHandler(StandardLocation.SOURCE_PATH, Option.SOURCEPATH),
new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_PATH, Option.PROCESSORPATH),
new OutputLocationHandler((StandardLocation.CLASS_OUTPUT), Option.D),
- new OutputLocationHandler((StandardLocation.SOURCE_OUTPUT), Option.S)
+ new OutputLocationHandler((StandardLocation.SOURCE_OUTPUT), Option.S),
+ new OutputLocationHandler((StandardLocation.NATIVE_HEADER_OUTPUT), Option.H)
};
for (LocationHandler h: handlers) {
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Fri Mar 09 17:10:56 2012 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Tue Mar 13 15:43:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -69,11 +69,11 @@
*/
private boolean verbose;
- /** Switch: scrable private names.
+ /** Switch: scramble private names.
*/
private boolean scramble;
- /** Switch: scrable private names.
+ /** Switch: scramble private names.
*/
private boolean scrambleAll;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/JNIWriter.java Tue Mar 13 15:43:40 2012 -0700
@@ -0,0 +1,856 @@
+/*
+ * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.javac.jvm;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+import java.util.StringTokenizer;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.SimpleTypeVisitor8;
+import javax.lang.model.util.Types;
+
+import javax.tools.FileObject;
+import javax.tools.JavaFileManager;
+import javax.tools.StandardLocation;
+
+import com.sun.tools.javac.code.Attribute;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Kinds;
+import com.sun.tools.javac.code.Scope;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.model.JavacElements;
+import com.sun.tools.javac.model.JavacTypes;
+import com.sun.tools.javac.util.Assert;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.util.Options;
+
+import static com.sun.tools.javac.main.Option.*;
+
+/** This class provides operations to write native header files for classes.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own risk.
+ * This code and its internal interfaces are subject to change or
+ * deletion without notice.</b>
+ */
+public class JNIWriter {
+ protected static final Context.Key<JNIWriter> jniWriterKey =
+ new Context.Key<JNIWriter>();
+
+ /** Access to files. */
+ private final JavaFileManager fileManager;
+
+ JavacElements elements;
+ JavacTypes types;
+
+ /** The log to use for verbose output.
+ */
+ private final Log log;
+
+ /** Switch: verbose output.
+ */
+ private boolean verbose;
+
+ /** Switch: check all nested classes of top level class
+ */
+ private boolean checkAll;
+
+ private Mangle mangler;
+
+ private Context context;
+
+ private Symtab syms;
+
+ private String lineSep;
+
+ private final boolean isWindows =
+ System.getProperty("os.name").startsWith("Windows");
+
+ /** Get the ClassWriter instance for this context. */
+ public static JNIWriter instance(Context context) {
+ JNIWriter instance = context.get(jniWriterKey);
+ if (instance == null)
+ instance = new JNIWriter(context);
+ return instance;
+ }
+
+ /** Construct a class writer, given an options table.
+ */
+ private JNIWriter(Context context) {
+ context.put(jniWriterKey, this);
+ fileManager = context.get(JavaFileManager.class);
+ log = Log.instance(context);
+
+ Options options = Options.instance(context);
+ verbose = options.isSet(VERBOSE);
+ checkAll = options.isSet("javah:full");
+
+ this.context = context; // for lazyInit()
+ syms = Symtab.instance(context);
+
+ lineSep = System.getProperty("line.separator");
+ }
+
+ private void lazyInit() {
+ if (mangler == null) {
+ elements = JavacElements.instance(context);
+ types = JavacTypes.instance(context);
+ mangler = new Mangle(elements, types);
+ }
+ }
+
+ public boolean needsHeader(ClassSymbol c) {
+ if (c.isLocal() || (c.flags() & Flags.SYNTHETIC) != 0)
+ return false;
+
+ if (checkAll)
+ return needsHeader(c.outermostClass(), true);
+ else
+ return needsHeader(c, false);
+ }
+
+ private boolean needsHeader(ClassSymbol c, boolean checkNestedClasses) {
+ if (c.isLocal() || (c.flags() & Flags.SYNTHETIC) != 0)
+ return false;
+
+ for (Attribute.Compound a: c.attributes_field) {
+ if (a.type.tsym == syms.nativeHeaderType.tsym)
+ return true;
+ }
+ for (Scope.Entry i = c.members_field.elems; i != null; i = i.sibling) {
+ if (i.sym.kind == Kinds.MTH && (i.sym.flags() & Flags.NATIVE) != 0)
+ return true;
+ }
+ if (checkNestedClasses) {
+ for (Scope.Entry i = c.members_field.elems; i != null; i = i.sibling) {
+ if ((i.sym.kind == Kinds.TYP) && needsHeader(((ClassSymbol) i.sym), true))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Emit a class file for a given class.
+ * @param c The class from which a class file is generated.
+ */
+ public FileObject write(ClassSymbol c)
+ throws IOException
+ {
+ String className = c.flatName().toString();
+ FileObject outFile
+ = fileManager.getFileForOutput(StandardLocation.NATIVE_HEADER_OUTPUT,
+ "", className.replaceAll("[.$]", "_") + ".h", null);
+ Writer out = outFile.openWriter();
+ try {
+ write(out, c);
+ if (verbose)
+ log.printVerbose("wrote.file", outFile);
+ out.close();
+ out = null;
+ } finally {
+ if (out != null) {
+ // if we are propogating an exception, delete the file
+ out.close();
+ outFile.delete();
+ outFile = null;
+ }
+ }
+ return outFile; // may be null if write failed
+ }
+
+ public void write(Writer out, ClassSymbol sym)
+ throws IOException {
+ lazyInit();
+ try {
+ String cname = mangler.mangle(sym.fullname, Mangle.Type.CLASS);
+ println(out, fileTop());
+ println(out, includes());
+ println(out, guardBegin(cname));
+ println(out, cppGuardBegin());
+
+ writeStatics(out, sym);
+ writeMethods(out, sym, cname);
+
+ println(out, cppGuardEnd());
+ println(out, guardEnd(cname));
+ } catch (TypeSignature.SignatureException e) {
+ throw new IOException(e);
+ }
+ }
+
+ protected void writeStatics(Writer out, ClassSymbol sym) throws IOException {
+ List<VariableElement> classfields = getAllFields(sym);
+
+ for (VariableElement v: classfields) {
+ if (!v.getModifiers().contains(Modifier.STATIC))
+ continue;
+ String s = null;
+ s = defineForStatic(sym, v);
+ if (s != null) {
+ println(out, s);
+ }
+ }
+ }
+
+ /**
+ * Including super class fields.
+ */
+ List<VariableElement> getAllFields(TypeElement subclazz) {
+ List<VariableElement> fields = new ArrayList<VariableElement>();
+ TypeElement cd = null;
+ Stack<TypeElement> s = new Stack<TypeElement>();
+
+ cd = subclazz;
+ while (true) {
+ s.push(cd);
+ TypeElement c = (TypeElement) (types.asElement(cd.getSuperclass()));
+ if (c == null)
+ break;
+ cd = c;
+ }
+
+ while (!s.empty()) {
+ cd = s.pop();
+ fields.addAll(ElementFilter.fieldsIn(cd.getEnclosedElements()));
+ }
+
+ return fields;
+ }
+
+ protected String defineForStatic(TypeElement c, VariableElement f) {
+ CharSequence cnamedoc = c.getQualifiedName();
+ CharSequence fnamedoc = f.getSimpleName();
+
+ String cname = mangler.mangle(cnamedoc, Mangle.Type.CLASS);
+ String fname = mangler.mangle(fnamedoc, Mangle.Type.FIELDSTUB);
+
+ Assert.check(f.getModifiers().contains(Modifier.STATIC));
+
+ if (f.getModifiers().contains(Modifier.FINAL)) {
+ Object value = null;
+
+ value = f.getConstantValue();
+
+ if (value != null) { /* so it is a ConstantExpression */
+ String constString = null;
+ if ((value instanceof Integer)
+ || (value instanceof Byte)
+ || (value instanceof Short)) {
+ /* covers byte, short, int */
+ constString = value.toString() + "L";
+ } else if (value instanceof Boolean) {
+ constString = ((Boolean) value) ? "1L" : "0L";
+ } else if (value instanceof Character) {
+ Character ch = (Character) value;
+ constString = String.valueOf(((int) ch) & 0xffff) + "L";
+ } else if (value instanceof Long) {
+ // Visual C++ supports the i64 suffix, not LL.
+ if (isWindows)
+ constString = value.toString() + "i64";
+ else
+ constString = value.toString() + "LL";
+ } else if (value instanceof Float) {
+ /* bug for bug */
+ float fv = ((Float)value).floatValue();
+ if (Float.isInfinite(fv))
+ constString = ((fv < 0) ? "-" : "") + "Inff";
+ else
+ constString = value.toString() + "f";
+ } else if (value instanceof Double) {
+ /* bug for bug */
+ double d = ((Double)value).doubleValue();
+ if (Double.isInfinite(d))
+ constString = ((d < 0) ? "-" : "") + "InfD";
+ else
+ constString = value.toString();
+ }
+
+ if (constString != null) {
+ StringBuilder s = new StringBuilder("#undef ");
+ s.append(cname); s.append("_"); s.append(fname); s.append(lineSep);
+ s.append("#define "); s.append(cname); s.append("_");
+ s.append(fname); s.append(" "); s.append(constString);
+ return s.toString();
+ }
+
+ }
+ }
+
+ return null;
+ }
+
+
+ protected void writeMethods(Writer out, ClassSymbol sym, String cname)
+ throws IOException, TypeSignature.SignatureException {
+ List<ExecutableElement> classmethods = ElementFilter.methodsIn(sym.getEnclosedElements());
+ for (ExecutableElement md: classmethods) {
+ if(md.getModifiers().contains(Modifier.NATIVE)){
+ TypeMirror mtr = types.erasure(md.getReturnType());
+ String sig = signature(md);
+ TypeSignature newtypesig = new TypeSignature(elements);
+ CharSequence methodName = md.getSimpleName();
+ boolean longName = false;
+ for (ExecutableElement md2: classmethods) {
+ if ((md2 != md)
+ && (methodName.equals(md2.getSimpleName()))
+ && (md2.getModifiers().contains(Modifier.NATIVE)))
+ longName = true;
+
+ }
+ println(out, "/*");
+ println(out, " * Class: " + cname);
+ println(out, " * Method: " +
+ mangler.mangle(methodName, Mangle.Type.FIELDSTUB));
+ println(out, " * Signature: " + newtypesig.getTypeSignature(sig, mtr));
+ println(out, " */");
+ println(out, "JNIEXPORT " + jniType(mtr) +
+ " JNICALL " +
+ mangler.mangleMethod(md, sym,
+ (longName) ?
+ Mangle.Type.METHOD_JNI_LONG :
+ Mangle.Type.METHOD_JNI_SHORT));
+ print(out, " (JNIEnv *, ");
+ List<? extends VariableElement> paramargs = md.getParameters();
+ List<TypeMirror> args = new ArrayList<TypeMirror>();
+ for (VariableElement p: paramargs) {
+ args.add(types.erasure(p.asType()));
+ }
+ if (md.getModifiers().contains(Modifier.STATIC))
+ print(out, "jclass");
+ else
+ print(out, "jobject");
+
+ for (TypeMirror arg: args) {
+ print(out, ", ");
+ print(out, jniType(arg));
+ }
+ println(out, ");"
+ + lineSep);
+ }
+ }
+ }
+
+ // c.f. MethodDoc.signature
+ String signature(ExecutableElement e) {
+ StringBuilder sb = new StringBuilder("(");
+ String sep = "";
+ for (VariableElement p: e.getParameters()) {
+ sb.append(sep);
+ sb.append(types.erasure(p.asType()).toString());
+ sep = ",";
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+ protected final String jniType(TypeMirror t) {
+ TypeElement throwable = elements.getTypeElement("java.lang.Throwable");
+ TypeElement jClass = elements.getTypeElement("java.lang.Class");
+ TypeElement jString = elements.getTypeElement("java.lang.String");
+ Element tclassDoc = types.asElement(t);
+
+
+ switch (t.getKind()) {
+ case ARRAY: {
+ TypeMirror ct = ((ArrayType) t).getComponentType();
+ switch (ct.getKind()) {
+ case BOOLEAN: return "jbooleanArray";
+ case BYTE: return "jbyteArray";
+ case CHAR: return "jcharArray";
+ case SHORT: return "jshortArray";
+ case INT: return "jintArray";
+ case LONG: return "jlongArray";
+ case FLOAT: return "jfloatArray";
+ case DOUBLE: return "jdoubleArray";
+ case ARRAY:
+ case DECLARED: return "jobjectArray";
+ default: throw new Error(ct.toString());
+ }
+ }
+
+ case VOID: return "void";
+ case BOOLEAN: return "jboolean";
+ case BYTE: return "jbyte";
+ case CHAR: return "jchar";
+ case SHORT: return "jshort";
+ case INT: return "jint";
+ case LONG: return "jlong";
+ case FLOAT: return "jfloat";
+ case DOUBLE: return "jdouble";
+
+ case DECLARED: {
+ if (tclassDoc.equals(jString))
+ return "jstring";
+ else if (types.isAssignable(t, throwable.asType()))
+ return "jthrowable";
+ else if (types.isAssignable(t, jClass.asType()))
+ return "jclass";
+ else
+ return "jobject";
+ }
+ }
+
+ Assert.check(false, "jni unknown type");
+ return null; /* dead code. */
+ }
+
+ protected String fileTop() {
+ return "/* DO NOT EDIT THIS FILE - it is machine generated */";
+ }
+
+ protected String includes() {
+ return "#include <jni.h>";
+ }
+
+ /*
+ * Deal with the C pre-processor.
+ */
+ protected String cppGuardBegin() {
+ return "#ifdef __cplusplus" + lineSep
+ + "extern \"C\" {" + lineSep
+ + "#endif";
+ }
+
+ protected String cppGuardEnd() {
+ return "#ifdef __cplusplus" + lineSep
+ + "}" + lineSep
+ + "#endif";
+ }
+
+ protected String guardBegin(String cname) {
+ return "/* Header for class " + cname + " */" + lineSep
+ + lineSep
+ + "#ifndef _Included_" + cname + lineSep
+ + "#define _Included_" + cname;
+ }
+
+ protected String guardEnd(String cname) {
+ return "#endif";
+ }
+
+ protected void print(Writer out, String text) throws IOException {
+ out.write(text);
+ }
+
+ protected void println(Writer out, String text) throws IOException {
+ out.write(text);
+ out.write(lineSep);
+ }
+
+
+ private static class Mangle {
+
+ public static class Type {
+ public static final int CLASS = 1;
+ public static final int FIELDSTUB = 2;
+ public static final int FIELD = 3;
+ public static final int JNI = 4;
+ public static final int SIGNATURE = 5;
+ public static final int METHOD_JDK_1 = 6;
+ public static final int METHOD_JNI_SHORT = 7;
+ public static final int METHOD_JNI_LONG = 8;
+ };
+
+ private Elements elems;
+ private Types types;
+
+ Mangle(Elements elems, Types types) {
+ this.elems = elems;
+ this.types = types;
+ }
+
+ public final String mangle(CharSequence name, int mtype) {
+ StringBuilder result = new StringBuilder(100);
+ int length = name.length();
+
+ for (int i = 0; i < length; i++) {
+ char ch = name.charAt(i);
+ if (isalnum(ch)) {
+ result.append(ch);
+ } else if ((ch == '.') &&
+ mtype == Mangle.Type.CLASS) {
+ result.append('_');
+ } else if (( ch == '$') &&
+ mtype == Mangle.Type.CLASS) {
+ result.append('_');
+ result.append('_');
+ } else if (ch == '_' && mtype == Mangle.Type.FIELDSTUB) {
+ result.append('_');
+ } else if (ch == '_' && mtype == Mangle.Type.CLASS) {
+ result.append('_');
+ } else if (mtype == Mangle.Type.JNI) {
+ String esc = null;
+ if (ch == '_')
+ esc = "_1";
+ else if (ch == '.')
+ esc = "_";
+ else if (ch == ';')
+ esc = "_2";
+ else if (ch == '[')
+ esc = "_3";
+ if (esc != null) {
+ result.append(esc);
+ } else {
+ result.append(mangleChar(ch));
+ }
+ } else if (mtype == Mangle.Type.SIGNATURE) {
+ if (isprint(ch)) {
+ result.append(ch);
+ } else {
+ result.append(mangleChar(ch));
+ }
+ } else {
+ result.append(mangleChar(ch));
+ }
+ }
+
+ return result.toString();
+ }
+
+ public String mangleMethod(ExecutableElement method, TypeElement clazz,
+ int mtype) throws TypeSignature.SignatureException {
+ StringBuilder result = new StringBuilder(100);
+ result.append("Java_");
+
+ if (mtype == Mangle.Type.METHOD_JDK_1) {
+ result.append(mangle(clazz.getQualifiedName(), Mangle.Type.CLASS));
+ result.append('_');
+ result.append(mangle(method.getSimpleName(),
+ Mangle.Type.FIELD));
+ result.append("_stub");
+ return result.toString();
+ }
+
+ /* JNI */
+ result.append(mangle(getInnerQualifiedName(clazz), Mangle.Type.JNI));
+ result.append('_');
+ result.append(mangle(method.getSimpleName(),
+ Mangle.Type.JNI));
+ if (mtype == Mangle.Type.METHOD_JNI_LONG) {
+ result.append("__");
+ String typesig = signature(method);
+ TypeSignature newTypeSig = new TypeSignature(elems);
+ String sig = newTypeSig.getTypeSignature(typesig, method.getReturnType());
+ sig = sig.substring(1);
+ sig = sig.substring(0, sig.lastIndexOf(')'));
+ sig = sig.replace('/', '.');
+ result.append(mangle(sig, Mangle.Type.JNI));
+ }
+
+ return result.toString();
+ }
+ //where
+ private String getInnerQualifiedName(TypeElement clazz) {
+ return elems.getBinaryName(clazz).toString();
+ }
+
+ public final String mangleChar(char ch) {
+ String s = Integer.toHexString(ch);
+ int nzeros = 5 - s.length();
+ char[] result = new char[6];
+ result[0] = '_';
+ for (int i = 1; i <= nzeros; i++)
+ result[i] = '0';
+ for (int i = nzeros+1, j = 0; i < 6; i++, j++)
+ result[i] = s.charAt(j);
+ return new String(result);
+ }
+
+ // Warning: duplicated in Gen
+ private String signature(ExecutableElement e) {
+ StringBuilder sb = new StringBuilder();
+ String sep = "(";
+ for (VariableElement p: e.getParameters()) {
+ sb.append(sep);
+ sb.append(types.erasure(p.asType()).toString());
+ sep = ",";
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+ /* Warning: Intentional ASCII operation. */
+ private static boolean isalnum(char ch) {
+ return ch <= 0x7f && /* quick test */
+ ((ch >= 'A' && ch <= 'Z') ||
+ (ch >= 'a' && ch <= 'z') ||
+ (ch >= '0' && ch <= '9'));
+ }
+
+ /* Warning: Intentional ASCII operation. */
+ private static boolean isprint(char ch) {
+ return ch >= 32 && ch <= 126;
+ }
+ }
+
+ private static class TypeSignature {
+ static class SignatureException extends Exception {
+ private static final long serialVersionUID = 1L;
+ SignatureException(String reason) {
+ super(reason);
+ }
+ }
+
+ Elements elems;
+
+ /* Signature Characters */
+
+ private static final String SIG_VOID = "V";
+ private static final String SIG_BOOLEAN = "Z";
+ private static final String SIG_BYTE = "B";
+ private static final String SIG_CHAR = "C";
+ private static final String SIG_SHORT = "S";
+ private static final String SIG_INT = "I";
+ private static final String SIG_LONG = "J";
+ private static final String SIG_FLOAT = "F";
+ private static final String SIG_DOUBLE = "D";
+ private static final String SIG_ARRAY = "[";
+ private static final String SIG_CLASS = "L";
+
+
+
+ public TypeSignature(Elements elems){
+ this.elems = elems;
+ }
+
+ /*
+ * Returns the type signature of a field according to JVM specs
+ */
+ public String getTypeSignature(String javasignature) throws SignatureException {
+ return getParamJVMSignature(javasignature);
+ }
+
+ /*
+ * Returns the type signature of a method according to JVM specs
+ */
+ public String getTypeSignature(String javasignature, TypeMirror returnType)
+ throws SignatureException {
+ String signature = null; //Java type signature.
+ String typeSignature = null; //Internal type signature.
+ List<String> params = new ArrayList<String>(); //List of parameters.
+ String paramsig = null; //Java parameter signature.
+ String paramJVMSig = null; //Internal parameter signature.
+ String returnSig = null; //Java return type signature.
+ String returnJVMType = null; //Internal return type signature.
+ int dimensions = 0; //Array dimension.
+
+ int startIndex = -1;
+ int endIndex = -1;
+ StringTokenizer st = null;
+ int i = 0;
+
+ // Gets the actual java signature without parentheses.
+ if (javasignature != null) {
+ startIndex = javasignature.indexOf("(");
+ endIndex = javasignature.indexOf(")");
+ }
+
+ if (((startIndex != -1) && (endIndex != -1))
+ &&(startIndex+1 < javasignature.length())
+ &&(endIndex < javasignature.length())) {
+ signature = javasignature.substring(startIndex+1, endIndex);
+ }
+
+ // Separates parameters.
+ if (signature != null) {
+ if (signature.indexOf(",") != -1) {
+ st = new StringTokenizer(signature, ",");
+ if (st != null) {
+ while (st.hasMoreTokens()) {
+ params.add(st.nextToken());
+ }
+ }
+ } else {
+ params.add(signature);
+ }
+ }
+
+ /* JVM type signature. */
+ typeSignature = "(";
+
+ // Gets indivisual internal parameter signature.
+ while (params.isEmpty() != true) {
+ paramsig = params.remove(i).trim();
+ paramJVMSig = getParamJVMSignature(paramsig);
+ if (paramJVMSig != null) {
+ typeSignature += paramJVMSig;
+ }
+ }
+
+ typeSignature += ")";
+
+ // Get internal return type signature.
+
+ returnJVMType = "";
+ if (returnType != null) {
+ dimensions = dimensions(returnType);
+ }
+
+ //Gets array dimension of return type.
+ while (dimensions-- > 0) {
+ returnJVMType += "[";
+ }
+ if (returnType != null) {
+ returnSig = qualifiedTypeName(returnType);
+ returnJVMType += getComponentType(returnSig);
+ } else {
+ System.out.println("Invalid return type.");
+ }
+
+ typeSignature += returnJVMType;
+
+ return typeSignature;
+ }
+
+ /*
+ * Returns internal signature of a parameter.
+ */
+ private String getParamJVMSignature(String paramsig) throws SignatureException {
+ String paramJVMSig = "";
+ String componentType ="";
+
+ if(paramsig != null){
+
+ if(paramsig.indexOf("[]") != -1) {
+ // Gets array dimension.
+ int endindex = paramsig.indexOf("[]");
+ componentType = paramsig.substring(0, endindex);
+ String dimensionString = paramsig.substring(endindex);
+ if(dimensionString != null){
+ while(dimensionString.indexOf("[]") != -1){
+ paramJVMSig += "[";
+ int beginindex = dimensionString.indexOf("]") + 1;
+ if(beginindex < dimensionString.length()){
+ dimensionString = dimensionString.substring(beginindex);
+ }else
+ dimensionString = "";
+ }
+ }
+ } else componentType = paramsig;
+
+ paramJVMSig += getComponentType(componentType);
+ }
+ return paramJVMSig;
+ }
+
+ /*
+ * Returns internal signature of a component.
+ */
+ private String getComponentType(String componentType) throws SignatureException {
+
+ String JVMSig = "";
+
+ if(componentType != null){
+ if(componentType.equals("void")) JVMSig += SIG_VOID ;
+ else if(componentType.equals("boolean")) JVMSig += SIG_BOOLEAN ;
+ else if(componentType.equals("byte")) JVMSig += SIG_BYTE ;
+ else if(componentType.equals("char")) JVMSig += SIG_CHAR ;
+ else if(componentType.equals("short")) JVMSig += SIG_SHORT ;
+ else if(componentType.equals("int")) JVMSig += SIG_INT ;
+ else if(componentType.equals("long")) JVMSig += SIG_LONG ;
+ else if(componentType.equals("float")) JVMSig += SIG_FLOAT ;
+ else if(componentType.equals("double")) JVMSig += SIG_DOUBLE ;
+ else {
+ if(!componentType.equals("")){
+ TypeElement classNameDoc = elems.getTypeElement(componentType);
+
+ if(classNameDoc == null){
+ throw new SignatureException(componentType);
+ }else {
+ String classname = classNameDoc.getQualifiedName().toString();
+ String newclassname = classname.replace('.', '/');
+ JVMSig += "L";
+ JVMSig += newclassname;
+ JVMSig += ";";
+ }
+ }
+ }
+ }
+ return JVMSig;
+ }
+
+ int dimensions(TypeMirror t) {
+ if (t.getKind() != TypeKind.ARRAY)
+ return 0;
+ return 1 + dimensions(((ArrayType) t).getComponentType());
+ }
+
+
+ String qualifiedTypeName(TypeMirror type) {
+ TypeVisitor<Name, Void> v = new SimpleTypeVisitor8<Name, Void>() {
+ @Override
+ public Name visitArray(ArrayType t, Void p) {
+ return t.getComponentType().accept(this, p);
+ }
+
+ @Override
+ public Name visitDeclared(DeclaredType t, Void p) {
+ return ((TypeElement) t.asElement()).getQualifiedName();
+ }
+
+ @Override
+ public Name visitPrimitive(PrimitiveType t, Void p) {
+ return elems.getName(t.toString());
+ }
+
+ @Override
+ public Name visitNoType(NoType t, Void p) {
+ if (t.getKind() == TypeKind.VOID)
+ return elems.getName("void");
+ return defaultAction(t, p);
+ }
+
+ @Override
+ public Name visitTypeVariable(TypeVariable t, Void p) {
+ return t.getUpperBound().accept(this, p);
+ }
+ };
+ return v.visit(type).toString();
+ }
+ }
+
+}
--- a/langtools/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java Fri Mar 09 17:10:56 2012 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java Tue Mar 13 15:43:40 2012 -0700
@@ -44,6 +44,8 @@
import javax.tools.DiagnosticListener;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
+
import static javax.tools.StandardLocation.CLASS_OUTPUT;
import com.sun.source.util.TaskEvent;
@@ -60,6 +62,7 @@
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.Log.WriterKind;
+
import static com.sun.tools.javac.main.Option.*;
import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*;
import static com.sun.tools.javac.util.ListBuffer.lb;
@@ -227,6 +230,10 @@
*/
protected ClassWriter writer;
+ /** The native header writer.
+ */
+ protected JNIWriter jniWriter;
+
/** The module for the symbol table entry phases.
*/
protected Enter enter;
@@ -330,6 +337,7 @@
reader = ClassReader.instance(context);
make = TreeMaker.instance(context);
writer = ClassWriter.instance(context);
+ jniWriter = JNIWriter.instance(context);
enter = Enter.instance(context);
todo = Todo.instance(context);
@@ -1450,8 +1458,13 @@
JavaFileObject file;
if (usePrintSource)
file = printSource(env, cdef);
- else
+ else {
+ if (fileManager.hasLocation(StandardLocation.NATIVE_HEADER_OUTPUT)
+ && jniWriter.needsHeader(cdef.sym)) {
+ jniWriter.write(cdef.sym);
+ }
file = genCode(env, cdef);
+ }
if (results != null && file != null)
results.add(file);
} catch (IOException ex) {
--- a/langtools/src/share/classes/com/sun/tools/javac/main/Option.java Fri Mar 09 17:10:56 2012 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/main/Option.java Tue Mar 13 15:43:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -160,6 +160,8 @@
S("-s", "opt.arg.directory", "opt.sourceDest", STANDARD, FILEMANAGER),
+ H("-h", "opt.arg.directory", "opt.headerDest", STANDARD, FILEMANAGER),
+
IMPLICIT("-implicit:", "opt.implicit", STANDARD, BASIC, ONEOF, "none", "class"),
ENCODING("-encoding", "opt.arg.encoding", "opt.encoding", STANDARD, FILEMANAGER) {
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/javac.properties Fri Mar 09 17:10:56 2012 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/javac.properties Tue Mar 13 15:43:40 2012 -0700
@@ -1,5 +1,5 @@
#
-# Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -61,6 +61,8 @@
Specify where to place generated class files
javac.opt.sourceDest=\
Specify where to place generated source files
+javac.opt.headerDest=\
+ Specify where to place generated native header files
javac.opt.J=\
Pass <flag> directly to the runtime system
javac.opt.encoding=\
--- a/langtools/src/share/classes/javax/tools/StandardLocation.java Fri Mar 09 17:10:56 2012 +0000
+++ b/langtools/src/share/classes/javax/tools/StandardLocation.java Tue Mar 13 15:43:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -66,7 +66,13 @@
* Location to search for platform classes. Sometimes called
* the boot class path.
*/
- PLATFORM_CLASS_PATH;
+ PLATFORM_CLASS_PATH,
+
+ /**
+ * Location of new native header files.
+ * @since 1.8
+ */
+ NATIVE_HEADER_OUTPUT;
/**
* Gets a location object with the given name. The following
@@ -97,6 +103,13 @@
public String getName() { return name(); }
public boolean isOutputLocation() {
- return this == CLASS_OUTPUT || this == SOURCE_OUTPUT;
+ switch (this) {
+ case CLASS_OUTPUT:
+ case SOURCE_OUTPUT:
+ case NATIVE_HEADER_OUTPUT:
+ return true;
+ default:
+ return false;
+ }
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/javax/tools/annotation/GenerateNativeHeader.java Tue Mar 13 15:43:40 2012 -0700
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javax.tools.annotation;
+
+import java.lang.annotation.*;
+import static java.lang.annotation.RetentionPolicy.*;
+import static java.lang.annotation.ElementType.*;
+
+/**
+ * An annotation used to indicate that a native header file
+ * should be generated for this class.
+ *
+ * Normally, the presence of native methods is a sufficient
+ * indication of the need for a native header file. However,
+ * in some cases, a class may contain constants of interest to
+ * native code, without containing any native methods.
+ *
+ * @since 1.8
+ */
+@Documented
+@Target(TYPE)
+@Retention(SOURCE)
+public @interface GenerateNativeHeader {
+}
--- a/langtools/test/tools/javac/diags/CheckResourceKeys.java Fri Mar 09 17:10:56 2012 +0000
+++ b/langtools/test/tools/javac/diags/CheckResourceKeys.java Tue Mar 13 15:43:40 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -259,6 +259,7 @@
"application.home", // in Paths.java
"env.class.path",
"line.separator",
+ "os.name",
"user.dir",
// file names
"ct.sym",
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/nativeHeaders/NativeHeaderTest.java Tue Mar 13 15:43:40 2012 -0700
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7150368
+ * @summary javac should include basic ability to generate native headers
+ */
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.api.JavacTool;
+
+public class NativeHeaderTest {
+ public static void main(String... args) throws Exception {
+ new NativeHeaderTest().run();
+ }
+
+ /** How to invoke javac. */
+ enum RunKind {
+ /** Use the command line entry point. */
+ CMD,
+ /** Use the JavaCompiler API. */
+ API
+ };
+
+ /** Which classes for which to generate headers. */
+ enum GenKind {
+ /** Just classes with native methods or the marker annotation. */
+ SIMPLE,
+ /** All appropriate classes within the top level class. */
+ FULL
+ };
+
+ // ---------- Test cases, invoked reflectively via run. ----------
+
+ @Test
+ void simpleTest(RunKind rk, GenKind gk) throws Exception {
+ List<File> files = new ArrayList<File>();
+ files.add(createFile("p/C.java",
+ "class C { native void m(); }"));
+
+ Set<String> expect = createSet("C.h");
+
+ test(rk, gk, files, expect);
+ }
+
+ @Test
+ void nestedClassTest(RunKind rk, GenKind gk) throws Exception {
+ List<File> files = new ArrayList<File>();
+ files.add(createFile("p/C.java",
+ "class C { static class Inner { native void m(); } }"));
+
+ Set<String> expect = createSet("C_Inner.h");
+ if (gk == GenKind.FULL) expect.add("C.h");
+
+ test(rk, gk, files, expect);
+ }
+
+ @Test
+ void localClassTest(RunKind rk, GenKind gk) throws Exception {
+ List<File> files = new ArrayList<File>();
+ files.add(createFile("p/C.java",
+ "class C { native void m(); void m2() { class Local { } } }"));
+
+ Set<String> expect = createSet("C.h");
+
+ test(rk, gk, files, expect);
+ }
+
+ @Test
+ void syntheticClassTest(RunKind rk, GenKind gk) throws Exception {
+ List<File> files = new ArrayList<File>();
+ files.add(createFile("p/C.java",
+ "class C {\n"
+ + " private C() { }\n"
+ + " class Inner extends C { native void m(); }\n"
+ + "}"));
+
+ Set<String> expect = createSet("C_Inner.h");
+ if (gk == GenKind.FULL) expect.add("C.h");
+
+ test(rk, gk, files, expect);
+
+ // double check the synthetic class was generated
+ checkEqual("generatedClasses",
+ createSet("C.class", "C$1.class", "C$Inner.class"),
+ createSet(classesDir.list()));
+ }
+
+ @Test
+ void annoTest(RunKind rk, GenKind gk) throws Exception {
+ List<File> files = new ArrayList<File>();
+ files.add(createFile("p/C.java",
+ "@javax.tools.annotation.GenerateNativeHeader class C { }"));
+
+ Set<String> expect = createSet("C.h");
+
+ test(rk, gk, files, expect);
+ }
+
+ @Test
+ void annoNestedClassTest(RunKind rk, GenKind gk) throws Exception {
+ List<File> files = new ArrayList<File>();
+ files.add(createFile("p/C.java",
+ "class C { @javax.tools.annotation.GenerateNativeHeader class Inner { } }"));
+
+ Set<String> expect = createSet("C_Inner.h");
+ if (gk == GenKind.FULL) expect.add("C.h");
+
+ test(rk, gk, files, expect);
+ }
+
+ /**
+ * The worker method for each test case.
+ * Compile the files and verify that exactly the expected set of header files
+ * is generated.
+ */
+ void test(RunKind rk, GenKind gk, List<File> files, Set<String> expect) throws Exception {
+ List<String> args = new ArrayList<String>();
+ if (gk == GenKind.FULL)
+ args.add("-XDjavah:full");
+
+ switch (rk) {
+ case CMD:
+ args.add("-d");
+ args.add(classesDir.getPath());
+ args.add("-h");
+ args.add(headersDir.getPath());
+ for (File f: files)
+ args.add(f.getPath());
+ int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]));
+ if (rc != 0)
+ throw new Exception("compilation failed, rc=" + rc);
+ break;
+
+ case API:
+ fm.setLocation(StandardLocation.SOURCE_PATH, Arrays.asList(srcDir));
+ fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(classesDir));
+ fm.setLocation(StandardLocation.NATIVE_HEADER_OUTPUT, Arrays.asList(headersDir));
+ JavacTask task = javac.getTask(null, fm, null, args, null,
+ fm.getJavaFileObjectsFromFiles(files));
+ if (!task.call())
+ throw new Exception("compilation failed");
+ break;
+ }
+
+ Set<String> found = createSet(headersDir.list());
+ checkEqual("header files", expect, found);
+ }
+
+ /** Marker annotation for test cases. */
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface Test { }
+
+ /** Combo test to run all test cases in all modes. */
+ void run() throws Exception {
+ javac = JavacTool.create();
+ fm = javac.getStandardFileManager(null, null, null);
+
+ for (RunKind rk: RunKind.values()) {
+ for (GenKind gk: GenKind.values()) {
+ for (Method m: getClass().getDeclaredMethods()) {
+ Annotation a = m.getAnnotation(Test.class);
+ if (a != null) {
+ init(rk, gk, m.getName());
+ try {
+ m.invoke(this, new Object[] { rk, gk });
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ throw (cause instanceof Exception) ? ((Exception) cause) : e;
+ }
+ System.err.println();
+ }
+ }
+ }
+ }
+ System.err.println(testCount + " tests" + ((errorCount == 0) ? "" : ", " + errorCount + " errors"));
+ if (errorCount > 0)
+ throw new Exception(errorCount + " errors found");
+ }
+
+ /**
+ * Init directories for a test case.
+ */
+ void init(RunKind rk, GenKind gk, String name) throws IOException {
+ System.err.println("Test " + rk + " " + gk + " " + name);
+ testCount++;
+
+ testDir = new File(rk.toString().toLowerCase() + "_" + gk.toString().toLowerCase() + "-" + name);
+ srcDir = new File(testDir, "src");
+ srcDir.mkdirs();
+ classesDir = new File(testDir, "classes");
+ classesDir.mkdirs();
+ headersDir = new File(testDir, "headers");
+ headersDir.mkdirs();
+ }
+
+ /** Create a source file with given body text. */
+ File createFile(String path, final String body) throws IOException {
+ File f = new File(srcDir, path);
+ f.getParentFile().mkdirs();
+ try (FileWriter out = new FileWriter(f)) {
+ out.write(body);
+ }
+ return f;
+ }
+
+ /** Convenience method to create a set of items. */
+ <T> Set<T> createSet(T... items) {
+ return new HashSet<T>(Arrays.asList(items));
+ }
+
+ /** Convenience method to check two values are equal, and report an error if not. */
+ <T> void checkEqual(String label, T expect, T found) {
+ if ((found == null) ? (expect == null) : found.equals(expect))
+ return;
+ System.err.println("Error: mismatch");
+ System.err.println(" expected: " + expect);
+ System.err.println(" found: " + found);
+ errorCount++;
+ }
+
+ // Shared across API test cases
+ JavacTool javac;
+ StandardJavaFileManager fm;
+
+ // Directories set up by init
+ File testDir;
+ File srcDir;
+ File classesDir;
+ File headersDir;
+
+ // Statistics
+ int testCount;
+ int errorCount;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/nativeHeaders/javahComparison/CompareTest.java Tue Mar 13 15:43:40 2012 -0700
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2007,2012 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7150368
+ * @summary javac should include basic ability to generate native headers
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class CompareTest {
+ public static void main(String... args) throws Exception {
+ new CompareTest().run();
+ }
+
+ void run() throws Exception {
+ File srcDir = new File(System.getProperty("test.src"));
+ File classesDir = new File("classes");
+ classesDir.mkdirs();
+ File javacHeaders = new File("headers.javac");
+ javacHeaders.mkdirs();
+ File javahHeaders = new File("headers.javah");
+ javahHeaders.mkdirs();
+
+ List<String> javacArgs = new ArrayList<String>();
+ javacArgs.add("-d");
+ javacArgs.add(classesDir.getPath());
+ javacArgs.add("-h");
+ javacArgs.add(javacHeaders.getPath());
+ javacArgs.add("-XDjavah:full");
+
+ for (File f: srcDir.listFiles()) {
+ if (f.getName().matches("TestClass[0-9]+\\.java")) {
+ sourceFileCount++;
+ javacArgs.add(f.getPath());
+ }
+ }
+
+ int rc = com.sun.tools.javac.Main.compile(javacArgs.toArray(new String[javacArgs.size()]));
+ if (rc != 0)
+ throw new Exception("javac failed; rc=" + rc);
+
+ List<String> javahArgs = new ArrayList<String>();
+ javahArgs.add("-d");
+ javahArgs.add(javahHeaders.getPath());
+
+ for (File f: classesDir.listFiles()) {
+ if (f.getName().endsWith(".class")) {
+ javahArgs.add(inferBinaryName(f));
+ }
+ }
+
+ PrintWriter pw = new PrintWriter(System.out, true);
+ rc = com.sun.tools.javah.Main.run(javahArgs.toArray(new String[javahArgs.size()]), pw);
+ if (rc != 0)
+ throw new Exception("javah failed; rc=" + rc);
+
+ compare(javahHeaders, javacHeaders);
+
+ int javahHeaderCount = javahHeaders.list().length;
+ int javacHeaderCount = javacHeaders.list().length;
+
+ System.out.println(sourceFileCount + " .java files found");
+ System.out.println(javacHeaderCount + " .h files generated by javac");
+ System.out.println(javahHeaderCount + " .h files generated by javah");
+ System.out.println(compareCount + " header files compared");
+
+ if (javacHeaderCount != javahHeaderCount || javacHeaderCount != compareCount)
+ error("inconsistent counts");
+
+ if (errors > 0)
+ throw new Exception(errors + " errors occurred");
+ }
+
+ String inferBinaryName(File file) {
+ String name = file.getName();
+ return name.substring(0, name.length() - ".class".length()).replace("$", ".");
+ }
+
+ /** Compare two directories.
+ * @param f1 The golden directory
+ * @param f2 The directory to be compared
+ */
+ void compare(File f1, File f2) {
+ compare(f1, f2, null);
+ }
+
+ /** Compare two files or directories
+ * @param f1 The golden directory
+ * @param f2 The directory to be compared
+ * @param p An optional path identifying a file within the two directories
+ */
+ void compare(File f1, File f2, String p) {
+ File f1p = (p == null ? f1 : new File(f1, p));
+ File f2p = (p == null ? f2 : new File(f2, p));
+ if (f1p.isDirectory() && f2p.isDirectory()) {
+ Set<String> children = new HashSet<String>();
+ children.addAll(Arrays.asList(f1p.list()));
+ children.addAll(Arrays.asList(f2p.list()));
+ for (String c: children) {
+ compare(f1, f2, new File(p, c).getPath()); // null-safe for p
+ }
+ }
+ else if (f1p.isFile() && f2p.isFile()) {
+ System.out.println("checking " + p);
+ compareCount++;
+ String s1 = read(f1p);
+ String s2 = read(f2p);
+ if (!s1.equals(s2)) {
+ System.out.println("File: " + f1p + "\n" + s1);
+ System.out.println("File: " + f2p + "\n" + s2);
+ error("Files differ: " + f1p + " " + f2p);
+ }
+ }
+ else if (f1p.exists() && !f2p.exists())
+ error("Only in " + f1 + ": " + p);
+ else if (f2p.exists() && !f1p.exists())
+ error("Only in " + f2 + ": " + p);
+ else
+ error("Files differ: " + f1p + " " + f2p);
+ }
+
+ private String read(File f) {
+ try {
+ return new String(Files.readAllBytes(f.toPath()));
+ } catch (IOException e) {
+ error("error reading " + f + ": " + e);
+ return "";
+ }
+ }
+
+ private void error(String msg) {
+ System.out.println(msg);
+ errors++;
+ }
+
+ private int errors;
+ private int compareCount;
+ private int sourceFileCount;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/nativeHeaders/javahComparison/TestClass1.java Tue Mar 13 15:43:40 2012 -0700
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.List;
+
+public class TestClass1 {
+ // simple types
+ byte b;
+ short s;
+ int i;
+ long l;
+ float f;
+ double d;
+ Object o;
+ String t;
+ List<String> g;
+
+ // constants
+ static final byte bc = 0;
+ static final short sc = 0;
+ static final int ic = 0;
+ static final long lc = 0;
+ static final float fc = 0;
+ static final double dc = 0;
+ static final Object oc = null;
+ static final String tc = "";
+ static final List<String> gc = null;
+
+ // simple arrays
+ byte[] ba;
+ short[] sa; // not handled corrected by javah v6
+ int[] ia;
+ long[] la;
+ float[] fa;
+ double[] da;
+ Object[] oa;
+ String[] ta;
+ List<String>[] ga;
+
+ // multidimensional arrays
+ byte[][] baa;
+ short[][] saa;
+ int[][] iaa;
+ long[][] laa;
+ float[][] faa;
+ double[][] daa;
+ Object[][] oaa;
+ String[][] taa;
+ List<String>[] gaa;
+
+ // simple Java methods
+ byte bm() { return 0; }
+ short sm() { return 0; }
+ int im() { return 0; }
+ long lm() { return 0; }
+ float fm() { return 0; }
+ double dm() { return 0; }
+ Object om() { return null; }
+ String tm() { return ""; }
+ List<String> gm() { return null; }
+ void vm() { }
+ byte[] bam() { return null; }
+ short[] sam() { return null; }
+ int[] iam() { return null; }
+ long[] lam() { return null; }
+ float[] fam() { return null; }
+ double[] dam() { return null; }
+ Object[] oam() { return null; }
+ String[] tam() { return null; }
+ List<String>[] gam() { return null; }
+ byte[][] baam() { return null; }
+ short[][] saam() { return null; }
+ int[][] iaam() { return null; }
+ long[][] laam() { return null; }
+ float[][] faam() { return null; }
+ double[][] daam() { return null; }
+ Object[][] oaam() { return null; }
+ String[][] taam() { return null; }
+ List<String>[] gaam() { return null; }
+
+ // simple native methods
+ native byte bmn();
+ native short smn();
+ native int imn();
+ native long lmn();
+ native float fmn();
+ native double dmn();
+ native Object omn();
+ native String tmn();
+ native List<String> gmn();
+ native void vmn();
+ native byte[] bamn();
+ native short[] samn();
+ native int[] iamn();
+ native long[] lamn();
+ native float[] famn();
+ native double[] damn();
+ native Object[] oamn();
+ native String[] tamn();
+ native List<String>[] gamn();
+ native byte[][] baamn();
+ native short[][] saamn();
+ native int[][] iaamn();
+ native long[][] laamn();
+ native float[][] faamn();
+ native double[][] daamn();
+ native Object[][] oaamn();
+ native String[][] taamn();
+ native List<String>[] gaamn();
+
+ // overloaded Java methods
+ byte bm1() { return 0; }
+ short sm1() { return 0; }
+ int im1() { return 0; }
+ long lm1() { return 0; }
+ float fm1() { return 0; }
+ double dm1() { return 0; }
+ Object om1() { return null; }
+ String tm1() { return ""; }
+ List<String> gm1() { return null; }
+ void vm1() { }
+
+ byte bm2(int i) { return 0; }
+ short sm2(int i) { return 0; }
+ int im2(int i) { return 0; }
+ long lm2(int i) { return 0; }
+ float fm2(int i) { return 0; }
+ double dm2(int i) { return 0; }
+ Object om2(int i) { return null; }
+ String tm2(int i) { return ""; }
+ List<String> gm2(int i) { return null; }
+ void vm2(int i) { }
+
+ // overloaded native methods
+ native byte bmn1();
+ native short smn1();
+ native int imn1();
+ native long lmn1();
+ native float fmn1();
+ native double dmn1();
+ native Object omn1();
+ native String tmn1();
+ native List<String> gmn1();
+ native void vmn1();
+
+ native byte bmn2(int i);
+ native short smn2(int i);
+ native int imn2(int i);
+ native long lmn2(int i);
+ native float fmn2(int i);
+ native double dmn2(int i);
+ native Object omn2(int i);
+ native String tmn2(int i);
+ native List<String> gmn2(int i);
+ native void vmn2(int i);
+
+ // arg types for Java methods
+ void mb(byte b) { }
+ void ms(short s) { }
+ void mi(int i) { }
+ void ml(long l) { }
+ void mf(float f) { }
+ void md(double d) { }
+ void mo(Object o) { }
+ void mt(String t) { }
+ void mg(List<String> g) { }
+
+ // arg types for native methods
+ native void mbn(byte b);
+ native void msn(short s);
+ native void min(int i);
+ native void mln(long l);
+ native void mfn(float f);
+ native void mdn(double d);
+ native void mon(Object o);
+ native void mtn(String t);
+ native void mgn(List<String> g);
+
+ static class Inner1 {
+ // simple types
+ byte b;
+ short s;
+ int i;
+ long l;
+ float f;
+ double d;
+ Object o;
+ String t;
+ List<String> g;
+
+ // constants
+ static final byte bc = 0;
+ static final short sc = 0;
+ static final int ic = 0;
+ static final long lc = 0;
+ static final float fc = 0;
+ static final double dc = 0;
+ static final Object oc = null;
+ static final String tc = "";
+ static final List<String> gc = null;
+
+ // simple arrays
+ byte[] ba;
+ // short[] sa; // not handled corrected by javah v6
+ int[] ia;
+ long[] la;
+ float[] fa;
+ double[] da;
+ Object[] oa;
+ String[] ta;
+ List<String>[] ga;
+
+ // multidimensional arrays
+ byte[][] baa;
+ short[][] saa;
+ int[][] iaa;
+ long[][] laa;
+ float[][] faa;
+ double[][] daa;
+ Object[][] oaa;
+ String[][] taa;
+ List<String>[] gaa;
+
+ // simple Java methods
+ byte bm() { return 0; }
+ short sm() { return 0; }
+ int im() { return 0; }
+ long lm() { return 0; }
+ float fm() { return 0; }
+ double dm() { return 0; }
+ Object om() { return null; }
+ String tm() { return ""; }
+ List<String> gm() { return null; }
+ void vm() { }
+
+ // simple native methods
+ native byte bmn();
+ native short smn();
+ native int imn();
+ native long lmn();
+ native float fmn();
+ native double dmn();
+ native Object omn();
+ native String tmn();
+ native List<String> gmn();
+ native void vmn();
+
+ // overloaded Java methods
+ byte bm1() { return 0; }
+ short sm1() { return 0; }
+ int im1() { return 0; }
+ long lm1() { return 0; }
+ float fm1() { return 0; }
+ double dm1() { return 0; }
+ Object om1() { return null; }
+ String tm1() { return ""; }
+ List<String> gm1() { return null; }
+ void vm1() { }
+
+ byte bm2(int i) { return 0; }
+ short sm2(int i) { return 0; }
+ int im2(int i) { return 0; }
+ long lm2(int i) { return 0; }
+ float fm2(int i) { return 0; }
+ double dm2(int i) { return 0; }
+ Object om2(int i) { return null; }
+ String tm2(int i) { return ""; }
+ List<String> gm2(int i) { return null; }
+ void vm2(int i) { }
+
+ // overloaded native methods
+ native byte bmn1();
+ native short smn1();
+ native int imn1();
+ native long lmn1();
+ native float fmn1();
+ native double dmn1();
+ native Object omn1();
+ native String tmn1();
+ native List<String> gmn1();
+ native void vmn1();
+
+ native byte bmn2(int i);
+ native short smn2(int i);
+ native int imn2(int i);
+ native long lmn2(int i);
+ native float fmn2(int i);
+ native double dmn2(int i);
+ native Object omn2(int i);
+ native String tmn2(int i);
+ native List<String> gmn2(int i);
+ native void vmn2(int i);
+
+ // arg types for Java methods
+ void mb(byte b) { }
+ void ms(short s) { }
+ void mi(int i) { }
+ void ml(long l) { }
+ void mf(float f) { }
+ void md(double d) { }
+ void mo(Object o) { }
+ void mt(String t) { }
+ void mg(List<String> g) { }
+
+ // arg types for native methods
+ native void mbn(byte b);
+ native void msn(short s);
+ native void min(int i);
+ native void mln(long l);
+ native void mfn(float f);
+ native void mdn(double d);
+ native void mon(Object o);
+ native void mtn(String t);
+ native void mgn(List<String> g);
+ }
+
+ class Inner2 {
+ // simple types
+ byte b;
+ short s;
+ int i;
+ long l;
+ float f;
+ double d;
+ Object o;
+ String t;
+ List<String> g;
+
+ // constants
+ static final byte bc = 0;
+ static final short sc = 0;
+ static final int ic = 0;
+ static final long lc = 0;
+ static final float fc = 0;
+ static final double dc = 0;
+ //static final Object oc = null;
+ static final String tc = "";
+ //static final List<String> gc = null;
+
+ // simple arrays
+ byte[] ba;
+ // short[] sa; // not handled corrected by javah v6
+ int[] ia;
+ long[] la;
+ float[] fa;
+ double[] da;
+ Object[] oa;
+ String[] ta;
+ List<String>[] ga;
+
+ // multidimensional arrays
+ byte[][] baa;
+ short[][] saa;
+ int[][] iaa;
+ long[][] laa;
+ float[][] faa;
+ double[][] daa;
+ Object[][] oaa;
+ String[][] taa;
+ List<String>[] gaa;
+
+ // simple Java methods
+ byte bm() { return 0; }
+ short sm() { return 0; }
+ int im() { return 0; }
+ long lm() { return 0; }
+ float fm() { return 0; }
+ double dm() { return 0; }
+ Object om() { return null; }
+ String tm() { return ""; }
+ List<String> gm() { return null; }
+ void vm() { }
+
+ // simple native methods
+ native byte bmn();
+ native short smn();
+ native int imn();
+ native long lmn();
+ native float fmn();
+ native double dmn();
+ native Object omn();
+ native String tmn();
+ native List<String> gmn();
+ native void vmn();
+
+ // overloaded Java methods
+ byte bm1() { return 0; }
+ short sm1() { return 0; }
+ int im1() { return 0; }
+ long lm1() { return 0; }
+ float fm1() { return 0; }
+ double dm1() { return 0; }
+ Object om1() { return null; }
+ String tm1() { return ""; }
+ List<String> gm1() { return null; }
+ void vm1() { }
+
+ byte bm2(int i) { return 0; }
+ short sm2(int i) { return 0; }
+ int im2(int i) { return 0; }
+ long lm2(int i) { return 0; }
+ float fm2(int i) { return 0; }
+ double dm2(int i) { return 0; }
+ Object om2(int i) { return null; }
+ String tm2(int i) { return ""; }
+ List<String> gm2(int i) { return null; }
+ void vm2(int i) { }
+
+ // overloaded native methods
+ native byte bmn1();
+ native short smn1();
+ native int imn1();
+ native long lmn1();
+ native float fmn1();
+ native double dmn1();
+ native Object omn1();
+ native String tmn1();
+ native List<String> gmn1();
+ native void vmn1();
+
+ native byte bmn2(int i);
+ native short smn2(int i);
+ native int imn2(int i);
+ native long lmn2(int i);
+ native float fmn2(int i);
+ native double dmn2(int i);
+ native Object omn2(int i);
+ native String tmn2(int i);
+ native List<String> gmn2(int i);
+ native void vmn2(int i);
+
+ // arg types for Java methods
+ void mb(byte b) { }
+ void ms(short s) { }
+ void mi(int i) { }
+ void ml(long l) { }
+ void mf(float f) { }
+ void md(double d) { }
+ void mo(Object o) { }
+ void mt(String t) { }
+ void mg(List<String> g) { }
+
+ // arg types for native methods
+ native void mbn(byte b);
+ native void msn(short s);
+ native void min(int i);
+ native void mln(long l);
+ native void mfn(float f);
+ native void mdn(double d);
+ native void mon(Object o);
+ native void mtn(String t);
+ native void mgn(List<String> g);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/nativeHeaders/javahComparison/TestClass2.java Tue Mar 13 15:43:40 2012 -0700
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.tools.annotation.GenerateNativeHeader;
+
+@GenerateNativeHeader
+public class TestClass2 {
+ byte b;
+ short s;
+ int i;
+ long l;
+ float f;
+ double d;
+ Object o;
+ String t;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/nativeHeaders/javahComparison/TestClass3.java Tue Mar 13 15:43:40 2012 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.tools.annotation.GenerateNativeHeader;
+
+@GenerateNativeHeader
+public class TestClass3 {
+ public int tc3;
+
+ public class Inner1 {
+ public int tc3i1;
+
+ public class Inner1A {
+ public int tc3i1i1a;
+ }
+
+ public class Inner1B {
+ public int tc3i1i1b;
+ }
+ }
+
+ public class Inner2 {
+ public int tc321;
+
+ public class Inner2A {
+ public int tc3i2i2a;
+ }
+
+ public class Inner2B {
+ public int tc3i2i2b;
+ }
+ }
+}
+