com.sun.tools.javah.LLNI Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jdk.compiler Show documentation
Show all versions of jdk.compiler Show documentation
Maven repackaging of OpenJDK's jdk.compiler module
The newest version!
/*
* Copyright (c) 2002, 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.javah;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
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.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVisitor;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.SimpleTypeVisitor8;
/*
* 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.
*
* @author Sucheta Dambalkar(Revised)
*/
public class LLNI extends Gen {
protected final char innerDelim = '$'; /* For inner classes */
protected Set doneHandleTypes;
List fields;
List methods;
private boolean doubleAlign;
private int padFieldNum = 0;
LLNI(boolean doubleAlign, Util util) {
super(util);
this.doubleAlign = doubleAlign;
}
protected String getIncludes() {
return "";
}
protected void write(OutputStream o, TypeElement clazz) throws Util.Exit {
try {
String cname = mangleClassName(clazz.getQualifiedName().toString());
PrintWriter pw = wrapWriter(o);
fields = ElementFilter.fieldsIn(clazz.getEnclosedElements());
methods = ElementFilter.methodsIn(clazz.getEnclosedElements());
generateDeclsForClass(pw, clazz, cname);
// FIXME check if errors occurred on the PrintWriter and throw exception if so
} catch (TypeSignature.SignatureException e) {
util.error("llni.sigerror", e.getMessage());
}
}
protected void generateDeclsForClass(PrintWriter pw,
TypeElement clazz, String cname)
throws TypeSignature.SignatureException, Util.Exit {
doneHandleTypes = new HashSet();
/* The following handle types are predefined in "typedefs.h". Suppress
inclusion in the output by generating them "into the blue" here. */
genHandleType(null, "java.lang.Class");
genHandleType(null, "java.lang.ClassLoader");
genHandleType(null, "java.lang.Object");
genHandleType(null, "java.lang.String");
genHandleType(null, "java.lang.Thread");
genHandleType(null, "java.lang.ThreadGroup");
genHandleType(null, "java.lang.Throwable");
pw.println("/* LLNI Header for class " + clazz.getQualifiedName() + " */" + lineSep);
pw.println("#ifndef _Included_" + cname);
pw.println("#define _Included_" + cname);
pw.println("#include \"typedefs.h\"");
pw.println("#include \"llni.h\"");
pw.println("#include \"jni.h\"" + lineSep);
forwardDecls(pw, clazz);
structSectionForClass(pw, clazz, cname);
methodSectionForClass(pw, clazz, cname);
pw.println("#endif");
}
protected void genHandleType(PrintWriter pw, String clazzname) {
String cname = mangleClassName(clazzname);
if (!doneHandleTypes.contains(cname)) {
doneHandleTypes.add(cname);
if (pw != null) {
pw.println("#ifndef DEFINED_" + cname);
pw.println(" #define DEFINED_" + cname);
pw.println(" GEN_HANDLE_TYPES(" + cname + ");");
pw.println("#endif" + lineSep);
}
}
}
protected String mangleClassName(String s) {
return s.replace('.', '_')
.replace('/', '_')
.replace(innerDelim, '_');
}
protected void forwardDecls(PrintWriter pw, TypeElement clazz)
throws TypeSignature.SignatureException {
TypeElement object = elems.getTypeElement("java.lang.Object");
if (clazz.equals(object))
return;
genHandleType(pw, clazz.getQualifiedName().toString());
TypeElement superClass = (TypeElement) (types.asElement(clazz.getSuperclass()));
if (superClass != null) {
String superClassName = superClass.getQualifiedName().toString();
forwardDecls(pw, superClass);
}
for (VariableElement field: fields) {
if (!field.getModifiers().contains(Modifier.STATIC)) {
TypeMirror t = types.erasure(field.asType());
TypeSignature newTypeSig = new TypeSignature(elems);
String tname = newTypeSig.qualifiedTypeName(t);
String sig = newTypeSig.getTypeSignature(tname);
if (sig.charAt(0) != '[')
forwardDeclsFromSig(pw, sig);
}
}
for (ExecutableElement method: methods) {
if (method.getModifiers().contains(Modifier.NATIVE)) {
TypeMirror retType = types.erasure(method.getReturnType());
String typesig = signature(method);
TypeSignature newTypeSig = new TypeSignature(elems);
String sig = newTypeSig.getTypeSignature(typesig, retType);
if (sig.charAt(0) != '[')
forwardDeclsFromSig(pw, sig);
}
}
}
protected void forwardDeclsFromSig(PrintWriter pw, String sig) {
int len = sig.length();
int i = sig.charAt(0) == '(' ? 1 : 0;
/* Skip the initial "(". */
while (i < len) {
if (sig.charAt(i) == 'L') {
int j = i + 1;
while (sig.charAt(j) != ';') j++;
genHandleType(pw, sig.substring(i + 1, j));
i = j + 1;
} else {
i++;
}
}
}
protected void structSectionForClass(PrintWriter pw,
TypeElement jclazz, String cname) {
String jname = jclazz.getQualifiedName().toString();
if (cname.equals("java_lang_Object")) {
pw.println("/* struct java_lang_Object is defined in typedefs.h. */");
pw.println();
return;
}
pw.println("#if !defined(__i386)");
pw.println("#pragma pack(4)");
pw.println("#endif");
pw.println();
pw.println("struct " + cname + " {");
pw.println(" ObjHeader h;");
pw.print(fieldDefs(jclazz, cname));
if (jname.equals("java.lang.Class"))
pw.println(" Class *LLNI_mask(cClass);" +
" /* Fake field; don't access (see oobj.h) */");
pw.println("};" + lineSep + lineSep + "#pragma pack()");
pw.println();
return;
}
private static class FieldDefsRes {
public String className; /* Name of the current class. */
public FieldDefsRes parent;
public String s;
public int byteSize;
public boolean bottomMost;
public boolean printedOne = false;
FieldDefsRes(TypeElement clazz, FieldDefsRes parent, boolean bottomMost) {
this.className = clazz.getQualifiedName().toString();
this.parent = parent;
this.bottomMost = bottomMost;
int byteSize = 0;
if (parent == null) this.s = "";
else this.s = parent.s;
}
}
/* Returns "true" iff added a field. */
private boolean doField(FieldDefsRes res, VariableElement field,
String cname, boolean padWord) {
String fieldDef = addStructMember(field, cname, padWord);
if (fieldDef != null) {
if (!res.printedOne) { /* add separator */
if (res.bottomMost) {
if (res.s.length() != 0)
res.s = res.s + " /* local members: */" + lineSep;
} else {
res.s = res.s + " /* inherited members from " +
res.className + ": */" + lineSep;
}
res.printedOne = true;
}
res.s = res.s + fieldDef;
return true;
}
// Otherwise.
return false;
}
private int doTwoWordFields(FieldDefsRes res, TypeElement clazz,
int offset, String cname, boolean padWord) {
boolean first = true;
List fields = ElementFilter.fieldsIn(clazz.getEnclosedElements());
for (VariableElement field: fields) {
TypeKind tk = field.asType().getKind();
boolean twoWords = (tk == TypeKind.LONG || tk == TypeKind.DOUBLE);
if (twoWords && doField(res, field, cname, first && padWord)) {
offset += 8; first = false;
}
}
return offset;
}
String fieldDefs(TypeElement clazz, String cname) {
FieldDefsRes res = fieldDefs(clazz, cname, true);
return res.s;
}
FieldDefsRes fieldDefs(TypeElement clazz, String cname,
boolean bottomMost){
FieldDefsRes res;
int offset;
boolean didTwoWordFields = false;
TypeElement superclazz = (TypeElement) types.asElement(clazz.getSuperclass());
if (superclazz != null) {
String supername = superclazz.getQualifiedName().toString();
res = new FieldDefsRes(clazz,
fieldDefs(superclazz, cname, false),
bottomMost);
offset = res.parent.byteSize;
} else {
res = new FieldDefsRes(clazz, null, bottomMost);
offset = 0;
}
List fields = ElementFilter.fieldsIn(clazz.getEnclosedElements());
for (VariableElement field: fields) {
if (doubleAlign && !didTwoWordFields && (offset % 8) == 0) {
offset = doTwoWordFields(res, clazz, offset, cname, false);
didTwoWordFields = true;
}
TypeKind tk = field.asType().getKind();
boolean twoWords = (tk == TypeKind.LONG || tk == TypeKind.DOUBLE);
if (!doubleAlign || !twoWords) {
if (doField(res, field, cname, false)) offset += 4;
}
}
if (doubleAlign && !didTwoWordFields) {
if ((offset % 8) != 0) offset += 4;
offset = doTwoWordFields(res, clazz, offset, cname, true);
}
res.byteSize = offset;
return res;
}
/* OVERRIDE: This method handles instance fields */
protected String addStructMember(VariableElement member, String cname,
boolean padWord) {
String res = null;
if (member.getModifiers().contains(Modifier.STATIC)) {
res = addStaticStructMember(member, cname);
// if (res == null) /* JNI didn't handle it, print comment. */
// res = " /* Inaccessible static: " + member + " */" + lineSep;
} else {
TypeMirror mt = types.erasure(member.asType());
if (padWord) res = " java_int padWord" + padFieldNum++ + ";" + lineSep;
res = " " + llniType(mt, false, false) + " " + llniFieldName(member);
if (isLongOrDouble(mt)) res = res + "[2]";
res = res + ";" + lineSep;
}
return res;
}
static private final boolean isWindows =
System.getProperty("os.name").startsWith("Windows");
/*
* This method only handles static final fields.
*/
protected String addStaticStructMember(VariableElement field, String cname) {
String res = null;
Object exp = null;
if (!field.getModifiers().contains(Modifier.STATIC))
return res;
if (!field.getModifiers().contains(Modifier.FINAL))
return res;
exp = field.getConstantValue();
if (exp != null) {
/* Constant. */
String cn = cname + "_" + field.getSimpleName();
String suffix = null;
long val = 0;
/* Can only handle int, long, float, and double fields. */
if (exp instanceof Byte
|| exp instanceof Short
|| exp instanceof Integer) {
suffix = "L";
val = ((Number)exp).intValue();
}
else if (exp instanceof Long) {
// Visual C++ supports the i64 suffix, not LL
suffix = isWindows ? "i64" : "LL";
val = ((Long)exp).longValue();
}
else if (exp instanceof Float) suffix = "f";
else if (exp instanceof Double) suffix = "";
else if (exp instanceof Character) {
suffix = "L";
Character ch = (Character) exp;
val = ((int) ch) & 0xffff;
}
if (suffix != null) {
// Some compilers will generate a spurious warning
// for the integer constants for Integer.MIN_VALUE
// and Long.MIN_VALUE so we handle them specially.
if ((suffix.equals("L") && (val == Integer.MIN_VALUE)) ||
(suffix.equals("LL") && (val == Long.MIN_VALUE))) {
res = " #undef " + cn + lineSep
+ " #define " + cn
+ " (" + (val + 1) + suffix + "-1)" + lineSep;
} else if (suffix.equals("L") || suffix.endsWith("LL")) {
res = " #undef " + cn + lineSep
+ " #define " + cn + " " + val + suffix + lineSep;
} else {
res = " #undef " + cn + lineSep
+ " #define " + cn + " " + exp + suffix + lineSep;
}
}
}
return res;
}
protected void methodSectionForClass(PrintWriter pw,
TypeElement clazz, String cname)
throws TypeSignature.SignatureException, Util.Exit {
String methods = methodDecls(clazz, cname);
if (methods.length() != 0) {
pw.println("/* Native method declarations: */" + lineSep);
pw.println("#ifdef __cplusplus");
pw.println("extern \"C\" {");
pw.println("#endif" + lineSep);
pw.println(methods);
pw.println("#ifdef __cplusplus");
pw.println("}");
pw.println("#endif");
}
}
protected String methodDecls(TypeElement clazz, String cname)
throws TypeSignature.SignatureException, Util.Exit {
String res = "";
for (ExecutableElement method: methods) {
if (method.getModifiers().contains(Modifier.NATIVE))
res = res + methodDecl(method, clazz, cname);
}
return res;
}
protected String methodDecl(ExecutableElement method,
TypeElement clazz, String cname)
throws TypeSignature.SignatureException, Util.Exit {
String res = null;
TypeMirror retType = types.erasure(method.getReturnType());
String typesig = signature(method);
TypeSignature newTypeSig = new TypeSignature(elems);
String sig = newTypeSig.getTypeSignature(typesig, retType);
boolean longName = needLongName(method, clazz);
if (sig.charAt(0) != '(')
util.error("invalid.method.signature", sig);
res = "JNIEXPORT " + jniType(retType) + " JNICALL" + lineSep + jniMethodName(method, cname, longName)
+ "(JNIEnv *, " + cRcvrDecl(method, cname);
List params = method.getParameters();
List argTypes = new ArrayList();
for (VariableElement p: params){
argTypes.add(types.erasure(p.asType()));
}
/* It would have been nice to include the argument names in the
declaration, but there seems to be a bug in the "BinaryField"
class, causing the getArguments() method to return "null" for
most (non-constructor) methods. */
for (TypeMirror argType: argTypes)
res = res + ", " + jniType(argType);
res = res + ");" + lineSep;
return res;
}
protected final boolean needLongName(ExecutableElement method,
TypeElement clazz) {
Name methodName = method.getSimpleName();
for (ExecutableElement memberMethod: methods) {
if ((memberMethod != method) &&
memberMethod.getModifiers().contains(Modifier.NATIVE) &&
(methodName.equals(memberMethod.getSimpleName())))
return true;
}
return false;
}
protected final String jniMethodName(ExecutableElement method, String cname,
boolean longName)
throws TypeSignature.SignatureException {
String res = "Java_" + cname + "_" + method.getSimpleName();
if (longName) {
TypeMirror mType = types.erasure(method.getReturnType());
List params = method.getParameters();
List argTypes = new ArrayList();
for (VariableElement param: params) {
argTypes.add(types.erasure(param.asType()));
}
res = res + "__";
for (TypeMirror t: argTypes) {
String tname = t.toString();
TypeSignature newTypeSig = new TypeSignature(elems);
String sig = newTypeSig.getTypeSignature(tname);
res = res + nameToIdentifier(sig);
}
}
return res;
}
// copied from JNI.java
protected final String jniType(TypeMirror t) throws Util.Exit {
TypeElement throwable = elems.getTypeElement("java.lang.Throwable");
TypeElement jClass = elems.getTypeElement("java.lang.Class");
TypeElement jString = elems.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";
}
}
util.bug("jni.unknown.type");
return null; /* dead code. */
}
protected String llniType(TypeMirror t, boolean handleize, boolean longDoubleOK) {
String res = null;
switch (t.getKind()) {
case ARRAY: {
TypeMirror ct = ((ArrayType) t).getComponentType();
switch (ct.getKind()) {
case BOOLEAN: res = "IArrayOfBoolean"; break;
case BYTE: res = "IArrayOfByte"; break;
case CHAR: res = "IArrayOfChar"; break;
case SHORT: res = "IArrayOfShort"; break;
case INT: res = "IArrayOfInt"; break;
case LONG: res = "IArrayOfLong"; break;
case FLOAT: res = "IArrayOfFloat"; break;
case DOUBLE: res = "IArrayOfDouble"; break;
case ARRAY:
case DECLARED: res = "IArrayOfRef"; break;
default: throw new Error(ct.getKind() + " " + ct);
}
if (!handleize) res = "DEREFERENCED_" + res;
break;
}
case VOID:
res = "void";
break;
case BOOLEAN:
case BYTE:
case CHAR:
case SHORT:
case INT:
res = "java_int" ;
break;
case LONG:
res = longDoubleOK ? "java_long" : "val32 /* java_long */";
break;
case FLOAT:
res = "java_float";
break;
case DOUBLE:
res = longDoubleOK ? "java_double" : "val32 /* java_double */";
break;
case DECLARED:
TypeElement e = (TypeElement) types.asElement(t);
res = "I" + mangleClassName(e.getQualifiedName().toString());
if (!handleize) res = "DEREFERENCED_" + res;
break;
default:
throw new Error(t.getKind() + " " + t); // FIXME
}
return res;
}
protected final String cRcvrDecl(Element field, String cname) {
return (field.getModifiers().contains(Modifier.STATIC) ? "jclass" : "jobject");
}
protected String maskName(String s) {
return "LLNI_mask(" + s + ")";
}
protected String llniFieldName(VariableElement field) {
return maskName(field.getSimpleName().toString());
}
protected final boolean isLongOrDouble(TypeMirror t) {
TypeVisitor v = new SimpleTypeVisitor8() {
public Boolean defaultAction(TypeMirror t, Void p){
return false;
}
public Boolean visitArray(ArrayType t, Void p) {
return visit(t.getComponentType(), p);
}
public Boolean visitPrimitive(PrimitiveType t, Void p) {
TypeKind tk = t.getKind();
return (tk == TypeKind.LONG || tk == TypeKind.DOUBLE);
}
};
return v.visit(t, null);
}
/* Do unicode to ansi C identifier conversion.
%%% This may not be right, but should be called more often. */
protected final String nameToIdentifier(String name) {
int len = name.length();
StringBuilder buf = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c = name.charAt(i);
if (isASCIILetterOrDigit(c))
buf.append(c);
else if (c == '/')
buf.append('_');
else if (c == '.')
buf.append('_');
else if (c == '_')
buf.append("_1");
else if (c == ';')
buf.append("_2");
else if (c == '[')
buf.append("_3");
else
buf.append("_0" + ((int)c));
}
return new String(buf);
}
protected final boolean isASCIILetterOrDigit(char c) {
if (((c >= 'A') && (c <= 'Z')) ||
((c >= 'a') && (c <= 'z')) ||
((c >= '0') && (c <= '9')))
return true;
else
return false;
}
}