Mercurial > hg > Members > kono > jpf-core
view src/main/gov/nasa/jpf/vm/Types.java @ 28:7be90179bb3b
Provided support for double colon operator used for lamabda expressions. Fixed a bug related to generating names for funcation object classes (by supporting double colon operator, a new stragety needed to generate unique names for function objects. To achive that the bootstrap ids are incorporated into names). Finally modified the method that retrieves the SAM from functional interfaces.
author | nastaran <nastaran.shafiei@gmail.com> |
---|---|
date | Thu, 25 Jun 2015 13:20:50 -0700 |
parents | 61d41facf527 |
children |
line wrap: on
line source
/* * Copyright (C) 2014, United States Government, as represented by the * Administrator of the National Aeronautics and Space Administration. * All rights reserved. * * The Java Pathfinder core (jpf-core) platform is licensed under the * Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package gov.nasa.jpf.vm; import gov.nasa.jpf.JPFException; import java.lang.reflect.Method; import java.util.ArrayList; /** * 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 getDequalifiedMethodSignature (String mName){ int idx = mName.indexOf('('); String sig = mName.substring(idx); return mName.substring(0, idx) + getDequalifiedArgumentSignature(sig); } public static String getDequalifiedArgumentSignature (String sig){ String[] argTypes = getArgumentTypeNames(sig); StringBuilder sb = new StringBuilder(); sb.append('('); for (int i=0; i<argTypes.length; i++){ if (i>0){ sb.append(','); } sb.append(dequalify(argTypes[i])); } sb.append(')'); return sb.toString(); } 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.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 ((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) { StringBuffer sb = new StringBuffer(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); } }