2009-10-30 6 views
1

Est-il possible de déterrer un nom de classe à partir du bytecode qui est formé à partir du code source de la classe?Résoudre le nom de classe à partir du bytecode

La situation est la suivante: je reçois un bytecode de classes à distance, quelque part, peu importe d'où il vient. Pour charger efficacement cette classe avec un classloader, je devrais avoir le nom de la classe aussi ... non?

+0

Eh bien, devinez quoi la JVM elle-même fait? Correct, il interprète le bytecode et obtient le nom de la classe :) Sans donner un peu plus de détails sur quoi, comment et (principalement) pourquoi vous essayez de le faire donner une réponse est difficile – sfussenegger

+0

Ouais, ouais, noté .. Je voudrais obtenir le nom de la classe à l'exécution afin que je puisse charger dynamiquement la classe avec un classloader, puisque vous devez spécifier le nom et le classpath de la classe que vous chargez. – JHollanti

Répondre

10

Si vous avez besoin juste le nom de la classe, il est probablement plus facile d'analyser le début de la classe fichier vous-même au lieu d'ajouter une bibliothèque 3ème partie pour la manipulation de code de classe juste à cet effet. Vous avez juste besoin des classes et des chaînes du pool constant, passez les drapeaux d'accès et remplacez/par. dans le nom de la classe. Si vous avez un tableau d'octets, vous pouvez appeler cette méthode avec new ByteArrayInputStream(byteArray):

public static String getClassName(InputStream is) throws Exception { 
    DataInputStream dis = new DataInputStream(is); 
    dis.readLong(); // skip header and class version 
    int cpcnt = (dis.readShort()&0xffff)-1; 
    int[] classes = new int[cpcnt]; 
    String[] strings = new String[cpcnt]; 
    for(int i=0; i<cpcnt; i++) { 
     int t = dis.read(); 
     if(t==7) classes[i] = dis.readShort()&0xffff; 
     else if(t==1) strings[i] = dis.readUTF(); 
     else if(t==5 || t==6) { dis.readLong(); i++; } 
     else if(t==8) dis.readShort(); 
     else dis.readInt(); 
    } 
    dis.readShort(); // skip access flags 
    return strings[classes[(dis.readShort()&0xffff)-1]-1].replace('/', '.'); 
} 
+0

Neat. Je n'avais pas regardé les détails, mais c'est plus succinct que ce à quoi je m'attendais. – McDowell

+0

Oui, ce n'est pas particulièrement difficile. Je me demande parfois pourquoi les gens suggèrent d'utiliser des bibliothèques tierces (comme dans ce cas ASM) pour résoudre des problèmes plutôt simples, lorsque le wrapup nécessite l'utilisation de la bibliothèque tierce elle-même est presque plus de code que ce qui serait nécessaire pour résoudre le problème. petit cerveau et l'API Java standard. Est-ce que les programmeurs ne peuvent plus programmer et sont-ils simplement capables de copier, coller et coller des librairies ensemble pour produire un résultat presque boulot? – jarnbjo

+1

Super, merci! Cela ressemble à une solution solide. Je n'aurai pas l'occasion de l'essayer immédiatement, mais je crois que cela fonctionne. "Les programmeurs ne peuvent plus programmer et sont-ils simplement ..." Dans une large mesure, oui;) Beaucoup de "codeurs" d'aujourd'hui ne sont pas tant des codeurs que des intégrateurs de bibliothèques. Plus (en particulier avec cette tâche) pour produire une solution sans erreur, il faudrait beaucoup de travail et d'étude car je ne suis pas du tout familier avec le bytecode Java. Mais merci! – JHollanti

1

Vous devriez pouvoir utiliser javap pour démonter le bytecode, si cela se produit qu'une seule fois dans un certain temps. Pour le faire lors de l'exécution: utilisez une bibliothèque de manipulation de code octet telle que BCEL d'Apache (http://jakarta.apache.org/bcel) pour analyser le code d'octet.

+0

N'y a-t-il pas de fonctionnalité intégrée pour cela? – JHollanti

+0

@JHollanti - ce que vous voulez est en arrière de la façon dont les choses sont faites habituellement - vous commencez avec un nom (souvent à partir d'un fichier descripteur) et ensuite utiliser des conventions de chemin pour charger les octets de classe. Il n'y a pas de moyen standard pour charger et exécuter plusieurs classes arbitraires (lequel serait le point d'entrée? Etc.) – McDowell

2

La façon la plus simple est d'utiliser probablement quelque chose comme ASM:

import org.objectweb.asm.ClassReader; 
import org.objectweb.asm.commons.EmptyVisitor; 

public class PrintClassName { 
    public static void main(String[] args) throws IOException { 
    class ClassNamePrinter extends EmptyVisitor { 
     @Override 
     public void visit(int version, int access, String name, String signature, 
      String superName, String[] interfaces) { 
     System.out.println("Class name: " + name); 
     } 
    } 

    InputStream binary = new FileInputStream(args[0]); 
    try { 
     ClassReader reader = new ClassReader(binary); 
     reader.accept(new ClassNamePrinter(), 0); 
    } finally { 
     binary.close(); 
    } 
    } 
} 

Si vous ne pouvez pas utiliser une bibliothèque 3ème partie, vous pourriez read the class file format yourself.

0

Je pense que vous pouvez utiliser la méthode ClassLoader.defineClass, dans une sous-classe de ClassLoader, pour obtenir l'objet de classe pour un bytecode donné. (Non testé)

0

Juste pour être complet, dans les cas où l'utilisation de la bibliothèque ASM5 est acceptable, l'appel suivant pourrait être utilisé pour obtenir le nom de la classe de son représentation d'octets.

public String readClassName(final byte[] typeAsByte) { 
    return new ClassReader(typeAsByte).getClassName().replace("/", "."); 
}