/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.vm.classmgr; import java.util.ArrayList; import org.jnode.vm.InternString; import org.jnode.vm.JvmType; import org.jnode.vm.facade.TypeSizeInfo; /** * <description> * * @author epr */ public class Signature { private String signature; private VmType[] parts; /** * Create a new instance * * @param signature * @param loader * @throws ClassNotFoundException */ public Signature(String signature, VmClassLoader loader) throws ClassNotFoundException { this.signature = signature; this.parts = split(signature.toCharArray(), loader); } /** * Is this the signature of a field? * * @return boolean */ public boolean isField() { return (signature.charAt(0) != '('); } /** * Is this the signature of a method? * * @return boolean */ public boolean isMethod() { return (signature.charAt(0) == '('); } /** * Gets the type of the signature (for field signatures) * * @return the type as a VmType */ public VmType getType() { return parts[0]; } /** * Gets the return type of the signature (for method signatures) * * @return the type as a VmType */ public VmType getReturnType() { return parts[parts.length - 1]; } /** * Gets the type of the parameter with the given index in this signature * (for method signatures) * * @param index * @return the type as a VmType */ public VmType getParamType(int index) { return parts[index]; } /** * Gets the number of parameters (for method signatures) * * @return the parameter count */ public int getParamCount() { return parts.length - 1; } /** * Calculate the number of argument slots that a method required, based of * the method signature supplied as a character array. * * @param typeSizeInfo gives the type sizes * @param signature the method signature as a character array * @return the slot count */ public static final int getArgSlotCount(TypeSizeInfo typeSizeInfo, char[] signature) { int ofs = 0; final int len = signature.length; if (signature[ofs++] != '(') { return 0; } int count = 0; while (ofs < len) { char ch = signature[ofs++]; switch (ch) { case ')': return count; case 'B': // Byte case 'Z': // Boolean case 'C': // Char case 'S': // Short case 'I': // Int count += typeSizeInfo.getStackSlots(JvmType.INT); break; case 'F': // Float count += typeSizeInfo.getStackSlots(JvmType.FLOAT); break; case 'D': // Double count += typeSizeInfo.getStackSlots(JvmType.DOUBLE); break; case 'J': // Long count += typeSizeInfo.getStackSlots(JvmType.LONG); break; case '[': // Array { count += typeSizeInfo.getStackSlots(JvmType.REFERENCE); while (signature[ofs] == '[') ofs++; if (signature[ofs] == 'L') { ofs++; while (signature[ofs] != ';') { ofs++; } } ofs++; break; } case 'L': // Object { count += typeSizeInfo.getStackSlots(JvmType.REFERENCE); while (signature[ofs] != ';') ofs++; ofs++; break; } } } throw new RuntimeException("Invalid signature in getArgSlotCount: " + String.valueOf(signature)); } /** * Calculate the number of argument slots that a method required, based of * the method signature supplied as a String. * * @param typeSizeInfo gives the type sizes * @param signature the method signature as a String * @return the slot count */ public static final int getArgSlotCount(TypeSizeInfo typeSizeInfo, String signature) { return getArgSlotCount(typeSizeInfo, signature.toCharArray()); } /** * Gets the stack slot number (0..) for a given java argument number. This * method takes into account the (per architecture) difference between the * size of longs, double and references and the number of slots they use. * * @param typeSizeInfo * @param method * @param javaArgIndex * @return the slot number */ public static final int getStackSlotForJavaArgNumber( TypeSizeInfo typeSizeInfo, VmMethod method, int javaArgIndex) { final int[] argTypes = JvmType.getArgumentTypes(method.getSignature()); final int argCount = argTypes.length; int stackSlot = 0; for (int i = 0; i < argCount; i++) { if (javaArgIndex == 0) { return stackSlot; } final int argJvmType = argTypes[i]; stackSlot += typeSizeInfo.getStackSlots(argJvmType); if ((argJvmType == JvmType.LONG) || (argJvmType == JvmType.DOUBLE)) { javaArgIndex -= 2; } else { javaArgIndex -= 1; } } return stackSlot + javaArgIndex; } private VmType[] split(char[] signature, VmClassLoader loader) throws ClassNotFoundException { ArrayList<VmType> list = new ArrayList<VmType>(); int ofs = 0; final int len = signature.length; if (signature[ofs] == '(') { ofs++; } while (ofs < len) { int start = ofs; char ch = signature[ofs++]; VmType vmClass; switch (ch) { case ')': continue; case 'B': // Byte case 'Z': // Boolean case 'C': // Char case 'S': // Short case 'I': // Int case 'F': // Float case 'D': // Double case 'J': // Long case 'V': // Void vmClass = VmType.getPrimitiveClass(ch); break; case '[': // Array { while (signature[ofs] == '[') { ofs++; } if (signature[ofs] == 'L') { ofs++; while (signature[ofs] != ';') { ofs++; } } ofs++; String sig = new String(signature, start, ofs - start).replace('/', '.'); sig = InternString.internString(sig); vmClass = loader.loadClass(sig, true); break; } case 'L': // Object { start++; while (signature[ofs] != ';') { ofs++; } String sig = new String(signature, start, ofs - start).replace('/', '.'); sig = InternString.internString(sig); ofs++; vmClass = loader.loadClass(sig, true); break; } default: throw new ClassFormatError("Unknown signature character " + ch); } if (vmClass == null) { throw new RuntimeException( "vmClass is null for signature character " + ch); } list.add(vmClass); } return (VmType[]) list.toArray(new VmType[list.size()]); } /** * Convert the given class to a signature * * @param cls * @return String */ public static String toSignature(Class<?> cls) { if (cls == null) { throw new NullPointerException("cls==null"); } if (cls.isArray()) { return '[' + toSignature(cls.getComponentType()); } else if (cls.isPrimitive()) { if (cls == Boolean.TYPE) { return "Z"; } else if (cls == Byte.TYPE) { return "B"; } else if (cls == Character.TYPE) { return "C"; } else if (cls == Short.TYPE) { return "S"; } else if (cls == Integer.TYPE) { return "I"; } else if (cls == Long.TYPE) { return "J"; } else if (cls == Float.TYPE) { return "F"; } else if (cls == Double.TYPE) { return "D"; } else if (cls == Void.TYPE) { return "V"; } return cls.getName(); } else { return 'L' + cls.getName().replace('.', '/') + ';'; } } /** * Convert the given class array to a signature * * @param returnType * @param argTypes * @return String */ public static String toSignature(Class returnType, Class[] argTypes) { StringBuilder b = new StringBuilder(); b.append('('); if (argTypes != null) { for (Class argType : argTypes) { b.append(toSignature(argType)); } } b.append(')'); if (returnType == null) { b.append('V'); } else { b.append(toSignature(returnType)); } return b.toString(); } /** * Convert the given VmType to a signature. * * @param cls a VmType instance * @return String the signature */ public static String toSignature(VmType cls) { if (cls == null) { throw new NullPointerException("cls==null"); } if (cls.isArray()) { return '[' + toSignature(((VmArrayClass) cls).getComponentType()); } else if (cls.isPrimitive()) { if (cls == VmType.getPrimitiveClass('Z')) { return "Z"; } else if (cls == VmType.getPrimitiveClass('B')) { return "B"; } else if (cls == VmType.getPrimitiveClass('C')) { return "C"; } else if (cls == VmType.getPrimitiveClass('S')) { return "S"; } else if (cls == VmType.getPrimitiveClass('I')) { return "I"; } else if (cls == VmType.getPrimitiveClass('J')) { return "J"; } else if (cls == VmType.getPrimitiveClass('F')) { return "F"; } else if (cls == VmType.getPrimitiveClass('D')) { return "D"; } else if (cls == VmType.getPrimitiveClass('V')) { return "V"; } return cls.getName(); } else { return 'L' + cls.getName().replace('.', '/') + ';'; } } /** * Convert the given VmType array to a signature. * * @param returnType * @param argTypes * @return String */ public static String toSignature(VmType returnType, VmType[] argTypes) { StringBuilder b = new StringBuilder(); b.append('('); if (argTypes != null) { for (VmType argType : argTypes) { b.append(toSignature(argType)); } } b.append(')'); if (returnType == null) { b.append('V'); } else { b.append(toSignature(returnType)); } return b.toString(); } }