//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.vm;
import java.lang.reflect.Method;
import java.util.ArrayList;
import gov.nasa.jpf.JPFException;
/**
* various type mangling/demangling routines
*
* This reflects the general type id mess in Java. We support the following:
*
* builtin type: byte - T_BOOLEAN and the like
* type name: String - according to JLS 6.7 ("int", "x.Y[]")
* type signature: String - like JNI ("I", "[Lx/Y;")
* type classname: String - e.g. "int", "[I", "x.Y", "[Lx.Y;"
*/
public class Types {
// these have the same values as the BCEL Constants since we don't want to break compiled code
public static final byte T_NONE = 0; // illegal type
public static final byte T_BOOLEAN = 4;
public static final byte T_BYTE = 8;
public static final byte T_CHAR = 5;
public static final byte T_SHORT = 9;
public static final byte T_INT = 10;
public static final byte T_LONG = 11;
public static final byte T_FLOAT = 6;
public static final byte T_DOUBLE = 7;
public static final byte T_REFERENCE = 14;
public static final byte T_ARRAY = 13; // <2do> do we need this in addition to T_REFERENCE?
public static final byte T_VOID = 12;
public static byte[] getArgumentTypes (String signature) {
int i;
int j;
int nArgs;
for (i = 1, nArgs = 0; signature.charAt(i) != ')'; nArgs++) {
i += getTypeLength(signature, i);
}
byte[] args = new byte[nArgs];
for (i = 1, j = 0; j < nArgs; j++) {
int end = i + getTypeLength(signature, i);
String arg = signature.substring(i, end);
i = end;
args[j] = getBuiltinTypeFromSignature(arg);
}
return args;
}
public static String[] getArgumentTypeNames (String signature) {
int len = signature.length();
if ((len > 1) && (signature.charAt(1) == ')')) {
return new String[0]; // 'no args' shortcut
}
ArrayList<String> a = new ArrayList<String>();
for (int i = 1; signature.charAt(i) != ')';) {
int end = i + getTypeLength(signature,i);
String arg = signature.substring(i, end);
i = end;
a.add(getTypeName(arg));
}
String[] typeNames = new String[a.size()];
a.toArray(typeNames);
return typeNames;
}
public static String dequalify (String typeName){
int idx = typeName.lastIndexOf('.');
if (idx > 0) {
return typeName.substring(idx + 1);
} else {
return typeName;
}
}
public static String getDequalifiedTypeName (String sig){
String tn = getTypeName(sig);
return dequalify(tn);
}
public static String getArgumentSignature (String[] typeNames, boolean qualified){
StringBuilder sb = new StringBuilder();
sb.append('(');
for (int i=0; i<typeNames.length; i++){
if (i>0){
sb.append(',');
}
String tn = getTypeName(typeNames[i]);
if (!qualified){
int idx = tn.lastIndexOf('.');
if (idx >0){
tn = tn.substring(idx+1);
}
}
sb.append( tn);
}
sb.append(')');
return sb.toString();
}
/**
* get size in stack slots (ints), excluding this
*/
public static int getArgumentsSize (String sig) {
int n = 0;
for (int i = 1; sig.charAt(i) != ')'; i++) {
switch (sig.charAt(i)) {
case 'L':
do i++; while (sig.charAt(i) != ';');
n++;
break;
case '[':
do i++; while (sig.charAt(i) == '[');
if (sig.charAt(i) == 'L') {
do i++; while (sig.charAt(i) != ';');
}
n++;
break;
case 'J':
case 'D':
// the two-slot types
n += 2;
break;
default:
// just one slot entry
n++;
}
}
return n;
}
public static String getArrayElementType (String type) {
if (type.charAt(0) != '[') {
throw new JPFException("not an array type: " + type);
}
return type.substring(1);
}
public static String getComponentTerminal (String type) {
if (type.charAt(0) != '[') {
throw new JPFException("not an array type: " + type);
}
if(isReferenceSignature(type)) {
return type.substring(type.indexOf('L') + 1 , type.indexOf(';'));
} else {
return type.substring(type.lastIndexOf('[') + 1);
}
}
public static byte getBuiltinTypeFromSignature (String signature) {
switch (signature.charAt(0)) {
case 'B':
return T_BYTE;
case 'C':
return T_CHAR;
case 'D':
return T_DOUBLE;
case 'F':
return T_FLOAT;
case 'I':
return T_INT;
case 'J':
return T_LONG;
case 'L':
return T_REFERENCE;
case 'S':
return T_SHORT;
case 'V':
return T_VOID;
case 'Z':
return T_BOOLEAN;
case '[':
return T_ARRAY;
}
throw new JPFException("invalid type string: " + signature);
}
/**
* get the argument type part of the signature out of a
* JNI mangled method name.
* Note this is not the complete signature, since we don't have a
* return type (which is superfluous since it's not overloading,
* but unfortunately part of the signature in the class file)
*/
public static String getJNISignature (String mangledName) {
int i = mangledName.indexOf("__");
String sig = null;
if (i > 0) {
int k = 0;
int r = mangledName.indexOf("__", i+2); // maybe there is a return type part
boolean gotReturnType = false;
int len = mangledName.length();
char[] buf = new char[len + 2];
buf[k++] = '(';
for (i += 2; i < len; i++) {
if (i == r) { // here comes the return type part (that's not JNI, only MJI
if ((i + 2) < len) {
i++;
buf[k++] = ')';
gotReturnType = true;
continue;
} else {
break;
}
}
char c = mangledName.charAt(i);
if (c == '_') {
i++;
if (i < len) {
c = mangledName.charAt(i);
switch (c) {
case '1':
buf[k++] = '_';
break;
case '2':
buf[k++] = ';';
break;
case '3':
buf[k++] = '[';
break;
default:
buf[k++] = '/';
buf[k++] = c;
}
} else {
buf[k++] = '/';
}
} else {
buf[k++] = c;
}
}
if (!gotReturnType) {
// if there was no return type spec, assume 'void'
buf[k++] = ')';
buf[k++] = 'V';
}
sig = new String(buf, 0, k);
}
// Hmm, maybe we should return "()V" instead of null, but that seems a bit too assuming
return sig;
}
public static String getJNIMangledMethodName (Method m) {
String name = m.getName();
Class<?>[] pt = m.getParameterTypes();
StringBuilder s = new StringBuilder(name.length() + (pt.length * 16));
s.append(name);
s.append("__");
// <2do> not very efficient, but we don't care for now
for (int i = 0; i < pt.length; i++) {
s.append(getJNITypeCode(pt[i].getName()));
}
// the return type part, which is not in JNI, but is required for
// handling covariant return types
Class<?> rt = m.getReturnType();
s.append("__");
s.append(getJNITypeCode(rt.getName()));
return s.toString();
}
public static String getJNIMangledMethodName (String cls, String name,
String signature) {
StringBuilder s = new StringBuilder(signature.length() + 10);
int i;
char c;
int slen = signature.length();
if (cls != null) {
s.append(cls.replace('.', '_'));
}
s.append(name);
s.append("__");
// as defined in the JNI specs
for (i = 1; i<slen; i++) {
c = signature.charAt(i);
switch (c) {
case '/':
s.append('_');
break;
case '_':
s.append("_1");
break;
case ';':
s.append("_2");
break;
case '[':
s.append("_3");
break;
case ')':
// the return type part - note this is not JNI, but saves us a lot of trouble with
// the covariant return types of Java 1.5
s.append("__");
break;
default:
s.append(c);
}
}
return s.toString();
}
/**
* return the name part of a JNI mangled method name (which is of
* course not completely safe - you should only use it if you know
* this is a JNI name)
*/
public static String getJNIMethodName (String mangledName) {
// note that's the first '__' group, which marks the beginning of the arg types
int i = mangledName.indexOf("__");
if (i > 0) {
return mangledName.substring(0, i);
} else {
return mangledName;
}
}
/**
* type is supposed to be Class.getName conforming, i.e.
*
* int -> int
* int[] -> [I
* String -> java.lang.String
* String[] -> [Ljava.lang.String;
* String[][] -> [[Ljava.lang.String;
*
* <2do> this is really not very efficient
*/
public static String getJNITypeCode (String type) {
StringBuilder sb = new StringBuilder(32);
int l = type.length() - 1;
int i;
// Class.getName arrays "[...type"
for ( i=0; type.charAt(i) == '['; i++){
sb.append("_3");
}
// conventional arrays "type[]..."
for (; type.charAt(l) == ']'; l -= 2) {
sb.append("_3");
}
type = type.substring(i, l + 1);
if (type.equals("int") || type.equals("I")) {
sb.append('I');
} else if (type.equals("long") || type.equals("J")) {
sb.append('J');
} else if (type.equals("boolean") || type.equals("Z")) {
sb.append('Z');
} else if (type.equals("char") || type.equals("C")) {
sb.append('C');
} else if (type.equals("byte") || type.equals("B")) {
sb.append('B');
} else if (type.equals("short") || type.equals("S")) {
sb.append('S');
} else if (type.equals("double") || type.equals("D")) {
sb.append('D');
} else if (type.equals("float") || type.equals("F")) {
sb.append('F');
} else if (type.equals("void") || type.equals("V")) { // for return types
sb.append('V');
} else { // reference type
if (type.charAt(0) != 'L'){
sb.append('L');
}
l = type.length();
for (i=0; i < l; i++) {
char c = type.charAt(i);
switch (c) {
case '.':
sb.append('_');
break;
case '_':
sb.append("_1");
break;
case ';':
break;
default:
sb.append(c);
}
}
sb.append("_2");
}
return sb.toString();
}
// this includes the return type part
public static int getNumberOfStackSlots (String signature, boolean isStatic) {
int nArgSlots = 0;
int n = isStatic ? 0 : 1;
int sigLen = signature.length();
for (int i = 1; i < sigLen; i++) {
switch (signature.charAt(i)) {
case ')' : // end of arg part, but there still might be a return type
nArgSlots = n;
n = 0;
break;
case 'L': // reference = 1 slot
i = signature.indexOf(';', i);
n++;
break;
case '[':
do i++; while (signature.charAt(i) == '[');
if (signature.charAt(i) == 'L') {
i = signature.indexOf(';', i);
}
n++;
break;
case 'J':
case 'D':
n+=2;
break;
default:
n++;
}
}
return Math.max(n, nArgSlots);
}
public static int getNumberOfArguments (String signature) {
int i,n;
int sigLen = signature.length();
for (i = 1, n = 0; i<sigLen; n++) {
switch (signature.charAt(i)) {
case ')' :
return n;
case 'L':
do i++; while (signature.charAt(i) != ';');
break;
case '[':
do i++; while (signature.charAt(i) == '[');
if (signature.charAt(i) == 'L') {
do i++; while (signature.charAt(i) != ';');
}
break;
default:
// just a single type char
}
i++;
}
assert (false) : "malformed signature: " + signature;
return n; // that would be a malformed signature
}
public static boolean isReferenceSignature(String signature){
return signature.charAt(signature.length()-1) == ';';
}
public static boolean isReference (String type) {
int t = getBuiltinTypeFromSignature(type);
return (t == T_ARRAY) || (t == T_REFERENCE);
}
public static boolean isArray (String type) {
int t = getBuiltinTypeFromSignature(type);
return (t == T_ARRAY);
}
public static byte getReturnBuiltinType (String signature) {
int i = signature.indexOf(')');
return getBuiltinTypeFromSignature(signature.substring(i + 1));
}
public static String getReturnTypeSignature(String signature){
int i = signature.indexOf(')');
return signature.substring(i + 1);
}
public static String getReturnTypeName (String signature){
int i = signature.indexOf(')');
return getTypeName(signature.substring(i+1));
}
public static String getTypeSignature (String type, boolean asDotNotation) {
String t = null;
int arrayDim = 0;
type = asDotNotation ? type.replace('/', '.') : type.replace('.', '/');
if ((type.charAt(0) == '[') || (type.endsWith(";"))) { // [[[L...;
t = type;
} else {
while (type.endsWith("[]")) { // type[][][]
type = type.substring(0, type.length() - 2);
arrayDim++;
}
if (type.equals("byte")) {
t = "B";
} else if (type.equals("char")) {
t = "C";
} else if (type.equals("short")) {
t = "S";
} else if (type.equals("int")) {
t = "I";
} else if (type.equals("float")) {
t = "F";
} else if (type.equals("long")) {
t = "J";
} else if (type.equals("double")) {
t = "D";
} else if (type.equals("boolean")) {
t = "Z";
} else if (type.equals("void")) {
t = "V";
} else if (type.endsWith(";")) {
t = type;
} else {
t = "L" + type + ';';
}
while (arrayDim-- > 0) {
t = "[" + t;
}
}
return t;
}
public static byte getBuiltinType(String typeName){
if (typeName.equals("byte")) {
return T_BYTE;
} else if (typeName.equals("char")) {
return T_CHAR;
} else if (typeName.equals("short")) {
return T_SHORT;
} else if (typeName.equals("int")) {
return T_INT;
} else if (typeName.equals("float")) {
return T_FLOAT;
} else if (typeName.equals("long")) {
return T_LONG;
} else if (typeName.equals("double")) {
return T_DOUBLE;
} else if (typeName.equals("boolean")) {
return T_BOOLEAN;
} else if (typeName.equals("void")) {
return T_VOID;
} else {
if (typeName.charAt(typeName.length()-1) == ']'){
return T_ARRAY;
} else {
return T_REFERENCE;
}
}
}
public static String getBoxedType (byte type) {
switch (type) {
case Types.T_BOOLEAN:
return "Boolean";
case Types.T_BYTE:
return "Byte";
case Types.T_CHAR:
return "Character";
case Types.T_SHORT:
return "Short";
case Types.T_INT:
return "Integer";
case Types.T_LONG:
return "Long";
case Types.T_FLOAT:
return "Float";
case Types.T_DOUBLE:
return "Double";
default:
return null;
}
}
public static byte getUnboxedType (String typeName){
if (typeName.startsWith("java.lang.")){
typeName = typeName.substring(10);
if (typeName.equals("Boolean")){
return T_BOOLEAN;
} else if (typeName.equals("Byte")){
return T_BYTE;
} else if (typeName.equals("Character")){
return T_CHAR;
} else if (typeName.equals("Short")){
return T_SHORT;
} else if (typeName.equals("Integer")){
return T_INT;
} else if (typeName.equals("Long")){
return T_LONG;
} else if (typeName.equals("Float")){
return T_FLOAT;
} else if (typeName.equals("Double")){
return T_DOUBLE;
}
}
// everything else can't be a box type
if (typeName.charAt(0) == '[' || typeName.charAt(typeName.length()-1) == ']'){
return T_ARRAY;
} else {
return T_REFERENCE;
}
}
public static String getClassNameFromSignature (String signature){
if (signature.charAt(signature.length()-1) == ';'){ // reference
return signature.replace('/', '.');
} else { // builtin
switch (signature.charAt(0)){
case 'Z': return "boolean";
case 'B': return "byte";
case 'C': return "char";
case 'S': return "short";
case 'I': return "int";
case 'J': return "long";
case 'F': return "float";
case 'D': return "double";
default:
throw new JPFException("illegal type signature: " + signature);
}
}
}
/**
* get the canonical representation of a type name, which happens to be
* (1) the name of the builtin type (e.g. "int")
* (2) the normal dot name for ordinary classes (e.g. "java.lang.String")
* (3) the coded dot name for arrays (e.g. "[B", "[[C", or "[Ljava.lang.String;")
*
* not sure if we need to support internal class names (which use '/'
* instead of '.' as separators
*
* no idea what's the logic behind this, but let's implement it
*/
public static String getClassNameFromTypeName (String typeName) {
typeName = typeName.replace('/','.');
int n = typeName.length()-1;
if (typeName.charAt(0) == '['){ // the "[<type>" notation
if (typeName.length() == 1) {
return "";
}
if (typeName.charAt(1) == 'L'){
if (typeName.charAt(n) != ';'){
typeName = typeName + ';';
}
}
return typeName;
}
int i=typeName.indexOf('[');
if (i>0){ // the sort of "<type>[]"
StringBuilder sb = new StringBuilder();
sb.append('[');
for (int j=i; (j=typeName.indexOf('[',j+1)) >0;){
sb.append('[');
}
typeName = typeName.substring(0,i);
if (isBasicType(typeName)){
sb.append( getTypeSignature(typeName, true));
} else {
sb.append('L');
sb.append(typeName);
sb.append(';');
}
return sb.toString();
}
if (typeName.charAt(n) == ';') {
return typeName.substring(1,n);
}
return typeName;
}
public static boolean isTypeCode (String t) {
char c = t.charAt(0);
if (c == '[') {
return true;
}
if ((t.length() == 1) &&
((c == 'B') || (c == 'I') || (c == 'S') || (c == 'C') ||
(c == 'F') || (c == 'J') || (c == 'D') || (c == 'Z'))) {
return true;
}
if (t.endsWith(";")) {
return true;
}
return false;
}
public static boolean isBasicType (String typeName){
return ("boolean".equals(typeName) ||
"byte".equals(typeName) ||
"char".equals(typeName) ||
"int".equals(typeName) ||
"long".equals(typeName) ||
"double".equals(typeName) ||
"short".equals(typeName) ||
"float".equals(typeName));
}
public static byte getTypeCode (String signature){
char c = signature.charAt(0);
switch (c) {
case 'B':
return T_BYTE;
case 'C':
return T_CHAR;
case 'D':
return T_DOUBLE;
case 'F':
return T_FLOAT;
case 'I':
return T_INT;
case 'J':
return T_LONG;
case 'L':
return T_REFERENCE;
case 'S':
return T_SHORT;
case 'V':
return T_VOID;
case 'Z':
return T_BOOLEAN;
case '[':
return T_ARRAY;
default:
throw new JPFException("unknow typecode: " + signature);
}
}
/**
* return the qualified signature name according to JLS 6.7 (e.g. "int", "x.Y[]")
*/
public static String getTypeName (String signature) {
int len = signature.length();
char c = signature.charAt(0);
if (len == 1) {
switch (c) {
case 'B':
return "byte";
case 'C':
return "char";
case 'D':
return "double";
case 'F':
return "float";
case 'I':
return "int";
case 'J':
return "long";
case 'S':
return "short";
case 'V':
return "void";
case 'Z':
return "boolean";
}
}
if (c == '[') {
return getTypeName(signature.substring(1)) + "[]";
}
int len1 = len-1;
if (signature.charAt(len1) == ';') {
return signature.substring(1, len1).replace('/', '.');
}
throw new JPFException("invalid type string: " + signature);
}
/** thoses are according to the arrayType codes of the newarray JVMS definition */
public static String getElementDescriptorOfType (int arrayType){
switch (arrayType){
case 4: return "Z";
case 5: return "C";
case 6: return "F";
case 7: return "D";
case 8: return "B";
case 9: return "S";
case 10: return "I";
case 11: return "J";
}
return null;
}
/**
* what would be the info size in bytes, not words
* (we ignore 64bit machines for now)
*/
public static int getTypeSizeInBytes (String signature) {
switch (signature.charAt(0)) {
case 'V':
return 0;
case 'Z': // that's a stretch, but we assume boolean uses the smallest addressable size
case 'B':
return 1;
case 'S':
case 'C':
return 2;
case 'L':
case '[':
case 'F':
case 'I':
return 4;
case 'D':
case 'J':
return 8;
}
throw new JPFException("invalid type string: " + signature);
}
public static int getTypeSize (String signature) {
switch (signature.charAt(0)) {
case 'V':
return 0;
case 'B':
case 'C':
case 'F':
case 'I':
case 'L':
case 'S':
case 'Z':
case '[':
return 1;
case 'D':
case 'J':
return 2;
}
throw new JPFException("invalid type string: " + signature);
}
public static int getTypeSize (byte typeCategory){
if (typeCategory == T_LONG || typeCategory == T_DOUBLE){
return 2;
} else {
return 1;
}
}
public static String asTypeName (String type) {
if (type.startsWith("[") || type.endsWith(";")) {
return getTypeName(type);
}
return type;
}
public static int booleanToInt (boolean b) {
return b ? 1 : 0;
}
public static long doubleToLong (double d) {
return Double.doubleToLongBits(d);
}
public static int floatToInt (float f) {
return Float.floatToIntBits(f);
}
public static int hiDouble (double d) {
return hiLong(Double.doubleToLongBits(d));
}
public static int hiLong (long l) {
return (int) (l >> 32);
}
public static boolean instanceOf (String type, String ofType) {
int bType = getBuiltinTypeFromSignature(type);
if (ofType.equals("Ljava.lang.Object;")) {
throw new RuntimeException("different identifiers are used");
}
if ((bType == T_ARRAY) && ofType.equals("Ljava/lang/Object;")) {
return true;
}
int bOfType = getBuiltinTypeFromSignature(ofType);
if (bType != bOfType) {
return false;
}
switch (bType) {
case T_ARRAY:
return instanceOf(type.substring(1), ofType.substring(1));
case T_REFERENCE:
ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(getTypeName(type));
return ci.isInstanceOf(getTypeName(ofType));
default:
return true;
}
}
public static boolean intToBoolean (int i) {
return i != 0;
}
public static float intToFloat (int i) {
return Float.intBitsToFloat(i);
}
public static double intsToDouble (int l, int h) {
long bits = ((long) h << 32) | (/*(long)*/ l & 0xFFFFFFFFL);
return Double.longBitsToDouble(bits);
}
public static long intsToLong (int l, int h) {
return ((long) h << 32) | (/*(long)*/ l & 0xFFFFFFFFL);
}
public static int loDouble (double d) {
return loLong(Double.doubleToLongBits(d));
}
public static int loLong (long l) {
return (int) (l & 0xFFFFFFFFL);
}
public static double longToDouble (long l) {
return Double.longBitsToDouble(l);
}
private static int getTypeLength (String signature, int idx) {
switch (signature.charAt(idx)) {
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'V':
case 'Z':
return 1;
case '[':
return 1 + getTypeLength(signature, idx + 1);
case 'L':
int semicolon = signature.indexOf(';', idx);
if (semicolon == -1) {
throw new JPFException("invalid type signature: " +
signature);
}
return semicolon - idx + 1;
}
throw new JPFException("invalid type signature");
}
/**
* return the JPF internal representation of a method signature that is given
* in dot-notation (like javap),
*
* e.g. "int foo(int[],java.lang.String)" -> "foo([ILjava/lang/String;)I"
*
*/
public static String getSignatureName (String methodDecl) {
StringBuilder sb = new StringBuilder(128);
String retType = null;
int i = methodDecl.indexOf('(');
if (i>0){
//--- name and return type
String[] a = methodDecl.substring(0, i).split(" ");
if (a.length > 0){
sb.append(a[a.length-1]);
if (a.length > 1){
retType = getTypeSignature(a[a.length-2], false);
}
}
//--- argument types
int j = methodDecl.lastIndexOf(')');
if (j > 0){
sb.append('(');
for (String type : methodDecl.substring(i+1,j).split(",")){
if (!type.isEmpty()){
type = type.trim();
if (!type.isEmpty()){
sb.append( getTypeSignature(type,false));
}
}
}
sb.append(')');
if (retType != null){
sb.append(retType);
}
return sb.toString();
}
}
throw new JPFException("invalid method declaration: " + methodDecl);
}
}