Heim  >  Artikel  >  Java  >  Beispiel einer Methode zum Implementieren eines Klassenparsers in Java

Beispiel einer Methode zum Implementieren eines Klassenparsers in Java

黄舟
黄舟Original
2017-09-15 10:16:001256Durchsuche

In diesem Artikel werden hauptsächlich Beispiele für Java-Klassenparser-Implementierungsmethoden durch die Analyse von Klassendateien vorgestellt. Es hat einen gewissen Referenzwert und Freunde, die es brauchen, können mehr darüber erfahren.

Kürzlich schreibe ich ein privates Projekt namens ClassAnalyzer. Der Zweck von ClassAnalyzer besteht darin, uns ein tiefgreifendes Verständnis des Designs und der Struktur von Java-Klassendateien zu vermitteln. Das Hauptgerüst und die Grundfunktionen wurden fertiggestellt und einige detaillierte Funktionen werden in Zukunft hinzugefügt. Tatsächlich stellt JDK bereits das Befehlszeilentool javap zum Dekompilieren von Klassendateien bereit, aber dieser Artikel wird meine Idee der Implementierung des Parsers klären.

Klassendatei

Als Träger von Klassen- oder Schnittstelleninformationen definiert jede Klassendatei vollständig eine Klasse. Damit Java-Programme „einmal geschrieben und überall ausgeführt werden“ können, enthält die Java Virtual Machine-Spezifikation strenge Vorschriften für Klassendateien. Die grundlegende Dateneinheit, aus der eine Klassendatei besteht, sind Bytes. Zwischen diesen Bytes gibt es keine Trennzeichen. Dadurch werden in der gesamten Klassendatei fast alle für den Programmbetrieb erforderlichen Daten dargestellt multiple Dargestellt durch aufeinanderfolgende Bytes.

Gemäß der Java Virtual Machine-Spezifikation verwendet die Klassendatei eine Pseudostruktur, die der C-Sprachstruktur ähnelt, um Daten zu speichern. In dieser Pseudostruktur gibt es nur zwei Datentypen: vorzeichenlose Zahlen und Tische. Die Java Virtual Machine-Spezifikation definiert u1, u2, u4 und u8 zur Darstellung vorzeichenloser Zahlen von 1 Byte, 2 Byte, 4 Byte und 8 Byte. Zur Beschreibung von Zahlen und Indizes können Referenzen, Mengen oder Zeichenfolgen verwendet werden. Eine Tabelle ist ein zusammengesetzter Datentyp, der aus mehreren vorzeichenlosen Zahlen oder anderen Tabellen als Datenelementen besteht. Die Tabelle wird verwendet, um Daten mit einer hierarchischen zusammengesetzten Struktur zu beschreiben, sodass die gesamte Klassendatei im Wesentlichen eine Tabelle ist. In ClassAnalyzer entsprechen Byte, Short, Int und Long den Datentypen u1, u2, u4 und u8. Die Klassendatei wird als die folgende Java-Klasse beschrieben.


public class ClassFile {
 public U4 magic;       // magic
 public U2 minorVersion;      // minor_version
 public U2 majorVersion;      // major_version
 public U2 constantPoolCount;    // constant_pool_count
 public ConstantPoolInfo[] cpInfo;   // cp_info
 public U2 accessFlags;      // access_flags
 public U2 thisClass;      // this_class
 public U2 superClass;      // super_class
 public U2 interfacesCount;     // interfaces_count
 public U2[] interfaces;      // interfaces
 public U2 fieldsCount;      // fields_count
 public FieldInfo[] fields;     // fields
 public U2 methodsCount;      // methods_count
 public MethodInfo[] methods;    // methods
 public U2 attributesCount;     // attributes_count
 public BasicAttributeInfo[] attributes;  // attributes
}

So analysieren Sie die verschiedenen Datenelemente, aus denen die Klassendatei besteht, z. B. magische Zahlen und Klassendateiversionen sowie andere Datenelemente und Zugriffsflags , Klassenindex, übergeordneter Klassenindex, sie belegen eine feste Anzahl von Bytes in jeder Klassendatei, und beim Parsen muss nur die entsprechende Anzahl von Bytes gelesen werden. Darüber hinaus müssen hauptsächlich vier Teile flexibel gehandhabt werden: Konstantenpool, Feldtabellensammlung, Methodentabellensammlung und Attributtabellensammlung. Felder und Methoden können ihre eigenen Attribute haben, und die Klasse selbst verfügt auch über entsprechende Attribute. Daher umfasst das Parsen der Feldtabellensammlung und Methodentabellensammlung auch das Parsen der Attributtabelle.

Der Konstantenpool nimmt einen großen Teil der Daten in der Klassendatei ein und wird zum Speichern aller Konstanteninformationen verwendet, einschließlich numerischer und Zeichenfolgenkonstanten, Klassennamen, Schnittstellennamen, Feldnamen, Methodennamen, usw. Die Java Virtual Machine-Spezifikation definiert mehrere Konstantentypen, von denen jeder seine eigene Struktur hat. Der Konstantenpool selbst ist eine Tabelle, und beim Parsen sind mehrere Punkte zu beachten.


Jeder Konstantentyp wird durch ein Tag vom Typ u1 identifiziert.


Die im Header angegebene Konstantenpoolgröße (constantPoolCount) ist um 1 größer als der tatsächliche Wert. Wenn beispielsweise konstantPoolCount gleich 47 ist, gibt es 46 Konstanten im Konstantenpool.


Der Indexbereich des Konstantenpools beginnt bei 1. Wenn beispielsweise „constantPoolCount“ gleich 47 ist, beträgt der Indexbereich des Konstantenpools 1 bis 46. Der Zweck des Designers, Element 0 leer zu lassen, besteht darin, auszudrücken, dass „kein konstantes Poolelement referenziert wird“.


Die Struktur der Konstante CONSTANT_Utf8_info enthält ein Tag vom Typ u1, eine Länge vom Typ u2 und Bytes, die aus Längen-U1-Typen bestehen. Die kontinuierlichen Daten der Längenbytes sind kontinuierliche Daten mit MUTF-8 (. Modifizierte UTF-8)-codierte Zeichenfolge. MUTF-8 ist nicht mit UTF-8 kompatibel. Es gibt zwei Hauptunterschiede: Erstens wird das Nullzeichen in 2 Bytes (0xC0 und 0x80) codiert; zweitens werden die Zusatzzeichen in Ersatzzeichen aufgeteilt und gemäß UTF separat codiert -16, die entsprechenden Details finden Sie hier (Variante UTF-8).


Attributtabellen werden verwendet, um Informationen zu beschreiben, die für bestimmte Szenarios spezifisch sind. Klassendateien, Feldtabellen und Methodentabellen verfügen alle über entsprechende Attributtabellensätze. Die Java Virtual Machine-Spezifikation definiert eine Vielzahl von Attributen, und ClassAnalyzer implementiert derzeit das Parsen häufig verwendeter Attribute. Im Gegensatz zu Datenelementen vom Typ Konstante verfügen Attribute nicht über ein Tag zur Identifizierung des Attributtyps, aber jedes Attribut enthält einen attribute_name_index vom Typ u2, der auf eine Konstante vom Typ CONSTANT_Utf8_info im Konstantenpool verweist, die den Namen des Attributs enthält. Beim Parsen von Attributen erkennt ClassAnalyzer den Typ des Attributs anhand des Attributnamens, der der Konstante entspricht, auf die durch attribute_name_index verwiesen wird.


Feldtabelle wird verwendet, um in einer Klasse oder Schnittstelle deklarierte Variablen zu beschreiben. Zu den Feldern gehören Variablen auf Klassenebene und Variablen auf Instanzebene. Die Struktur der Feldtabelle umfasst einen u2-Typ access_flags, einen u2-Typ name_index, einen u2-Typ descriptor_index, einen u2-Typ attributes_count und attributes_count attributes_info-Typattribute. Wir haben das Parsing von Attributtabellen bereits eingeführt. Die Parsing-Methode von Attributen stimmt mit der Parsing-Methode von Attributtabellen überein.


Class的文件方法表采用了和字段表相同的存储格式,只是access_flags对应的含义有所不同。方法表包含着一个重要的属性:Code属性。Code属性存储了Java代码编译成的字节码指令,在ClassAnalyzer中,Code对应的Java类如下所示(仅列出了类属性)


public class Code extends BasicAttributeInfo {
 private short maxStack;
 private short maxLocals;
 private long codeLength;
 private byte[] code;
 private short exceptionTableLength;
 private ExceptionInfo[] exceptionTable;
 private short attributesCount;
 private BasicAttributeInfo[] attributes;
 ...
 private class ExceptionInfo {
  public short startPc;
  public short endPc;
  public short handlerPc;
  public short catchType;
   ...
 }
}

在Code属性中,codeLength和code分别用于存储字节码长度和字节码指令,每条指令即一个字节(u1类型)。在虚拟机执行时,通过读取code中的一个个字节码,并将字节码翻译成相应的指令。另外,虽然codeLength是一个u4类型的值,但是实际上一个方法不允许超过65535条字节码指令。

代码实现

ClassAnalyzer的源码已放在了GitHub上。在ClassAnalyzer的README中,我以一个类的Class文件为例,对该Class文件的每个字节进行了分析,希望对大家的理解有所帮助。

Das obige ist der detaillierte Inhalt vonBeispiel einer Methode zum Implementieren eines Klassenparsers in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn