857 lines
31 KiB
Java
857 lines
31 KiB
Java
/*
|
|
* Copyright (c) 1999, 2013, 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 (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;
|
|
for (Attribute.Compound a: i.sym.getDeclarationAttributes()) {
|
|
if (a.type.tsym == syms.nativeHeaderType.tsym)
|
|
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();
|
|
}
|
|
}
|
|
|
|
}
|