نحن نجمع برنامجًا بسيطًا يعرض "Hello World" ونتصفح هيكله
لا أعتقد أن المقال سيكون مفيدًا بشكل كافٍ لأولئك الذين لا يعرفون بشكل سطحي الشكل الذي يبدو عليه الرمز الفرعي وكيف يعمل JVM معه (على سبيل المثال ، على الأقل أبسط التعليمات (المعرفة حول وجودهم)).
في الواقع ، ليس من الصعب للغاية. يكفي استخدام أداة javap
من JDK والنظر في الكود javap
.
وسوف نبدأ في تحليل بنية الرمز الثانوي لـ JVM
كتاب مفيد للغاية لهذا كان مواصفات JVM الرسمية - مواصفات Java Virtual Machine في oracle
للبدء ، قم بإنشاء برنامج بسيط:
public class Main { public static void main(String ... args) { System.out.println("Hello World"); } }
قم javac Main.java
مع فريق javac Main.java
وفعليًا عملية التفكيك
javap -c -v Main
Main.class
Classfile /C:/Users/Arthur/playground/java/jvm/Main.class Last modified 26.10.2019; size 413 bytes MD5 checksum 6449121a3bb611fee394e4f322401ee1 Compiled from "Main.java" public class Main minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18 // Hello World #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #21 // Main #6 = Class #22 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 main #12 = Utf8 ([Ljava/lang/String;)V #13 = Utf8 SourceFile #14 = Utf8 Main.java #15 = NameAndType #7:#8 // "<init>":()V #16 = Class #23 // java/lang/System #17 = NameAndType #24:#25 // out:Ljava/io/PrintStream; #18 = Utf8 Hello World #19 = Class #26 // java/io/PrintStream #20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V #21 = Utf8 Main #22 = Utf8 java/lang/Object #23 = Utf8 java/lang/System #24 = Utf8 out #25 = Utf8 Ljava/io/PrintStream; #26 = Utf8 java/io/PrintStream #27 = Utf8 println #28 = Utf8 (Ljava/lang/String;)V { public Main(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public static void main(java.lang.String...); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello World 5: invokevirtual #4// Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 4: 0 line 5: 8 } SourceFile: "Main.java"
هذا مجرد تمثيل لبيتكود يسهل على الشخص رؤيته من الكود الأصلي ، لكنه يبدو مختلفًا:
cafe babe 0000 0034 001d 0a00 0600 0f09 0010 0011 0800 120a 0013 0014 0700 1507 0016 0100 063c 696e 6974 3e01 0003 2829 5601 0004 436f 6465 0100 0f4c 696e 654e 756d 6265 7254 6162 6c65 0100 046d 6169 6e01 0016 285b 4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b 2956 0100 0a53 6f75 7263 6546 696c 6501 0009 4d61 696e 2e6a 6176 610c 0007 0008 0700 170c 0018 0019 0100 0b48 656c 6c6f 2057 6f72 6c64 0700 1a0c 001b 001c 0100 044d 6169 6e01 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 6374 0100 106a 6176 612f 6c61 6e67 2f53 7973 7465 6d01 0003 6f75 7401 0015 4c6a 6176 612f 696f 2f50 7269 6e74 5374 7265 616d 3b01 0013 6a61 7661 2f69 6f2f 5072 696e 7453 7472 6561 6d01 0007 7072 696e 746c 6e01 0015 284c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b29 5600 2100 0500 0600 0000 0000 0200 0100 0700 0800 0100 0900 0000 1d00 0100 0100 0000 052a b700 01b1 0000 0001 000a 0000 0006 0001 0000 0001 0089 000b 000c 0001 0009 0000 0025 0002 0001 0000 0009 b200 0212 03b6 0004 b100 0000 0100 0a00 0000 0a00 0200 0000 0400 0800 0500 0100 0d00 0000 0200 0e
(يمكنك فتح ملف .class
الخاص بك من خلال نص Sublime يشير إلى File-> Save with Encoding -> Hexademical)
سوف نعمل مع هذا الرمز.
لكن أولاً ، نحن بحاجة إلى تنسيقه حتى لا يتم الخلط في مكانه ، والرمز الفرعي ، في الواقع ، له هيكل صلب للغاية:
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; }
يمكنك العثور عليها في مواصفات JVM الفصل 4.1 هيكل ClassFile
كل شيء بسيط هنا - يشار إلى البعد بالبايت على اليسار ، والوصف على اليمين.
سنقوم بتحليل البايت كود بالسداسي عشري ، حيث يأخذ كل رقم 4 بتات ، وبالتالي ، لاثنين من البايتات - 4 أرقام وأربعة بايت - 8 أرقام.
سحر
السحر هو القيمة التي تحدد شكل فئتنا. إنه يساوي 0xCAFEBABE
، الذي له تاريخه الخاص في الخلق .
minor_version ، major_version
هذه هي إصدارات ملف class
الخاص بك. إذا قمنا بالاتصال بـ major_version
M و minor_version
m ، minor_version
على إصدار ملف class
بنا باسم Mm
الآن سأقدم على الفور أمثلة من برنامجنا "Hello World" لمعرفة كيفية استخدامها:
cafe babe -- magic 0000 -- minor_version 0034 -- major_version
يمكننا أن نرى ذلك في الشفرة المفككة ، ولكن بالفعل في نظام الأرقام العشرية:
... public class Main minor version: 0 major version: 52 flags: ACC_PUBLIC, ...
constant_pool_count
يشار هنا إلى عدد المتغيرات في التجمع الثابت. في الوقت نفسه ، إذا قررت كتابة التعليمات البرمجية في الرمز الثنائي الخالص ، فأنت بحاجة بالتأكيد إلى رصد قيمتها ، لأنه إذا حددت القيمة الخطأ ، فسيذهب البرنامج بالكامل إلى الجحيم (محددًا!).
أيضًا ، لا تنسَ أنه يجب أن تكتب هناك عدد_المتغيرات_ في ___ + 1
المجموع نحصل عليه:
cafe babe -- magic 0000 0034 -- version 001d -- constant_pool_count
ثابت_بول []
كل نوع متغير في التجمع الثابت له هيكله الخاص:
cp_info { u1 tag; u1 info[]; }
هنا كل شيء يجب القيام به بالتتابع. أولاً نقرأ tag
لمعرفة نوع المتغير ونوع هذا المتغير ننظر إلى الهيكل الذي تحتوي قيمته اللاحقة على info[]
يمكن العثور على جدول به علامات في مواصفات علامات 4.3 تجمّع ثابت .
في الواقع ، ها هو الجهاز اللوحي:
كما ذكرنا سابقًا ، لكل نوع ثابت هيكله الخاص.
هنا ، على سبيل المثال ، هو بنية CONSTANT_Class
:
CONSTANT_Class_info { u1 tag; u2 name_index; }
هيكل المجال والطريقة:
CONSTANT_Fieldref_info { u1 tag; u2 class_index; u2 name_and_type_index; } CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
من المهم أن نلاحظ هنا أن الهياكل المختلفة قد يكون لها أطوال مختلفة.
النظر في جزء من التعليمات البرمجية لدينا:
cafe babe 0000 0034 001d -- constant_pool_count 0a00 0600 0f09 0010 0011 0800 12 ...
لذلك ، نحن ننظر إلى بنية الثابت ونكتشف أن البايتة الأولى محجوزة لنوع الثابت. نرى هنا 0a
(10) - وبالتالي ، فهو CONSTANT_Methodref
نحن ننظر إلى هيكلها:
CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
بعد بايت واحد للعلامة ، نحتاج إلى 4 بايتات أخرى لـ class_index
و name_and_type_index
cafe babe 0000 0034 001d -- constant_pool_count 0a 0006 000f -- CONSTANT_Methodref 0900 1000 1108 0012 ...
حسنًا ، وجدنا إحدى قيم البركة المستمرة. المضي قدما. ننظر ، 09
- يعني نوع CONSTANT_Fieldref
نحصل على:
cafe babe 0000 0034 001d -- constant_pool_count 0a 0006 000f -- CONSTANT_Methodref 09 0010 0011 -- CONSTANT_Fieldref 08 0012 ...
قد تعتقد أن معظم الأنواع لها نفس الشكل ، ولكن هذا ليس هو الحال.
على سبيل المثال ، تبدو بنية النوع التالي CONSTANT_String
:
CONSTANT_String_info { u1 tag; u2 string_index; }
كل هذه الهياكل يمكن العثور عليها في الفصل 4.4 The Constant Pool.
الآن دعونا نرى ما هي أنواع info
داخل يعني نفسه.
عادةً ما تحتوي الأساليب التي تقع أسفل نمط *_index
على العنوان من جدول تجمع ثابت. على سبيل المثال ، class_index
لقيمة النوع CONSTANT_Class_info
و string_index
CONSTANT_Utf8_info
يمكننا أن نرى هذا في الكود المفكك:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18
0a 0006 000f -- CONSTANT_Methodref 09 0010 0011 -- CONSTANT_Fieldref 08 0012 -- CONSTANT_String
يمكنك أيضًا إبراز تمثيل الأرقام والسلاسل.
يمكنك أن تقرأ عن تمثيل الأرقام بدءًا من الفصل 4.4.4 ، ولكن في الوقت الحالي سنحلل الخطوط فقط ، حيث إن الأرقام غير مدرجة في برنامج Hello World
في الواقع ، هكذا يظهر الخط:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }
على سبيل المثال ، عالم الترحيب:
01 -- tag 000b -- length 48 65 6c 6c 6f 20 57 6f 72 6c 64 -- bytes[length] // H ello W orld
تحليل مجموعة كاملة من الثوابت bytecode ، نحصل على:
مجموعة كاملة من الثوابت -- [Constant Pool] -- methodref 0a 0006 000f -- fieldref 09 0010 0011 -- string 08 0012 -- methodref 0a 0013 0014 -- Class 07 0015 -- Class 07 0016 -- Utf8 01 0006 3c 69 6e 69 74 3e -- Utf8 01 0003 28 29 56 -- Utf8 01 0004 43 6f 64 65 -- Utf8 01 000f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 -- Utf8 01 0004 6d 61 69 6e -- Utf8 01 0016 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 -- Utf8 01 000a 53 6f 75 72 63 65 46 69 6c 65 -- Utf8 01 0009 4d 61 69 6e 2e 6a 61 76 61 -- NameAndType 0c 0007 0008 -- Class 07 0017 -- NameAndType 0c 0018 0019 -- Utf8 01 000b 48 65 6c 6c 6f 20 57 6f 72 6c 64 -- Class 07 001a -- NameAndType 0c 001b 001c -- Utf8 01 0004 4d 61 69 6e -- Utf8 01 0010 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 -- Utf8 01 0010 6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73 74 65 6d -- Utf8 01 0003 6f 75 74 -- Utf8 01 0015 4c 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 3b -- Utf8 01 0013 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d -- Utf8 01 0007 70 72 69 6e 74 6c 6e -- Utf8 01 0015 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 -- [Constant Pool END]
أيضًا ، يمكننا مقارنتها برمز مفكك:
Constant pool: #1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18 // Hello World #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #21 // Main #6 = Class #22 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 main #12 = Utf8 ([Ljava/lang/String;)V #13 = Utf8 SourceFile #14 = Utf8 Main.java #15 = NameAndType #7:#8 // "<init>":()V #16 = Class #23 // java/lang/System #17 = NameAndType #24:#25 // out:Ljava/io/PrintStream; #18 = Utf8 Hello World #19 = Class #26 // java/io/PrintStream #20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V #21 = Utf8 Main #22 = Utf8 java/lang/Object #23 = Utf8 java/lang/System #24 = Utf8 out #25 = Utf8 Ljava/io/PrintStream; #26 = Utf8 java/io/PrintStream #27 = Utf8 println #28 = Utf8 (Ljava/lang/String;)V
وبالتالي ، تحقق من تطابق كل شيء ، لأن javap
في الحقيقة ببساطة يعالج هذا javap
لنا في شكل منسق.
هناك حاجة إلى تجمع ثابت للحصول على التعليمات. على سبيل المثال:
public Main(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // 1 4: return
لمزيد من المعلومات حول جميع الأنواع في التجمع الثابت ، انظر الفصل 4.4 التجمع الثابت.
المضي قدما في هيكل ClassFile
access_flags
هذا قناع نقطي لخصائص المعدل.
this_class
يجب أن يحتوي على عنوان في this
الفئة. في حالتنا ، يقع في العنوان 5:
Constant pool: #1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18 // Hello World #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #21 // Main #6 = Class #22 // java/lang/Object ...
تجدر الإشارة إلى أن بنية هذا المتغير يجب أن تتوافق مع CONSTANT_Class_info
super_class
عنوان سلف الصف. في حالتنا ، تكون القيمة في العنوان #6
. حسنًا ، بنية قيمة CONSTANT_Class_info
مطلوبة أيضًا
يتم تعريف أسماء هذه الفئات في بنية ثابت CONSTANT_Utf8_info
. إذا نظرنا إلى الخلايا #21
ورقم #22
، فسنرى:
... #21 = Utf8 Main #22 = Utf8 java/lang/Object ...
بمعنى أنه في هذه الخلايا name_index
من البنية:
CONSTANT_Class_info { u1 tag; u2 name_index; }
interfaces_count ، fields_count
لم تكن موجودة في برنامجنا ، لذلك ستكون قيمها مساوية لـ 0000 ، ولن يكون هناك أي قيم لاحقة fields[]
، interfaces[]
.
قراءة المزيد 4.1 هيكل ClassFile
methods_count
عدد الطرق. على الرغم من أننا نرى في الكود طريقة واحدة في الفصل ، إلا أن هناك بالفعل اثنين منهم. بالإضافة إلى الطريقة main
، هناك أيضًا مُنشئ افتراضي. لذلك ، عددهم هو اثنين ، في حالتنا.
طرق []
يجب أن يتوافق كل عنصر مع بنية method_info
الموضحة في طرق الفصل 4.6
method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }
في كودنا الثانوي (المنسق ، مع التعليقات) يبدو كما يلي:
-- [methods] -- public Main(); 0001 --access_flags 0007 -- name_index 0008 -- descriptor_index 0001 -- attributes_count -- attribute_info 0009 -- attribute_name_index (Code) 0000 001d - attribute_length 0001 -- max_stack 0001 -- max_locals 0000 0005 -- code_length 2a b7 00 01 b1 -- code[] 0000 -- exception_table_length 0001 -- attributes_count; 000a -- attribute_name_index 0000 0006 -- attribute_length 00 01 00 00 00 01 -- public static void main(java.lang.String...); 0089 --access_flags 000b -- name_index 000c -- descriptor_index 0001 -- attributes_count -- attribute_info 0009 -- attribute_name_index (Code) 0000 0025 -- attribute_length 0002 -- max_stack 0001 -- max_locals 0000 0009 -- code_length b2 00 02 12 03 b6 00 04 b1 -- code[] 0000 -- exception_table_length 0001 -- attributes_count 000a -- attribute_name_index 0000 000a -- attribute_length 00 02 00 00 00 04 00 08 00 05 -- [methods END]
دعنا نحلل بنية الطرق بمزيد من التفصيل:
access_flags
قناع معدل. جدول 4.5 طريقة الوصول وعلامات الخاصية
كما يمكننا أن نرى من bytecode ، في طريقة public Main();
(المُنشئ) هو القناع 0001
، مما يعني ACC_PUBLIC
.
الآن دعونا نحاول تجميع الطريقة main
بأنفسنا. هنا ما لديه:
- عام - ACC_PUBLIC - 0x0001
- ثابت - ACC_STATIC - 0x0008
- سلسلة ... args - ACC_VARARGS - 0x0080
نحن نجمع القناع: 0x0001 + 0x0008 + 0x0080 = 0x0089
. لذلك حصلنا على access_flag
بالمناسبة ، ACC_VARARGS هو اختياري هنا ، بمعنى أننا إذا نحن
وسيطات String [] المستخدمة بدلاً من String ... args ، فإن هذه العلامة لن تكون
name_index
عنوان اسم الأسلوب ( CONSTANT_Utf8_info
) في التجمع الثابت. من المهم أن نلاحظ هنا أن اسم المُنشئ ليس الرئيسي ، ولكن <init>
، الموجود في الخلية رقم 7.
تعرّف على المزيد حول <init>
و <clinit>
في الفصل 2.9 طرق خاصة
descriptor_index
تحدث تقريبًا ، هذا عنوان يشير إلى مؤشر أسلوب. يحتوي هذا الواصف على نوع قيمة الإرجاع ونوع توقيعه.
أيضًا ، تستخدم JVM اختصارات مترجمة:
بشكل عام ، يبدو كما يلي:
( ParameterDescriptor* ) ReturnDescriptor
على سبيل المثال ، الطريقة التالية:
Object method(int i, double d, Thread t) {..}
يمكن أن تكون ممثلة
(IDLjava/lang/Thread;)Ljava/lang/Object
في الواقع ، I
int
، D
double
، و Ljava/lang/Thread;
فئة Thread
من مكتبة java.lang
القياسية.
بعد ذلك ، هناك سمات لها أيضًا هيكلها الخاص.
ولكن أولاً ، كما هو الحال دائمًا ، عدد attributes_count
ثم السمات نفسها مع الهيكل الموصوف في الفصل 4.7 السمات
attribute_info { u2 attribute_name_index; u4 attribute_length; u1 info[attribute_length]; }
attribute_name_index
يحدد اسم سمة. في حالتنا ، كلتا الطريقتين لديها Code
. السمات هي موضوع كبير منفصل يمكنك من خلاله إنشاء سماتك الخاصة من خلال المواصفات. ولكن الآن ، يجب أن نعرف أن attribute_name_index
يشير فقط إلى العنوان في التجمع الثابت مع بنية CONSTANT_Utf8_info
attribute_length
يحتوي على طول السمة ، وليس بما في ذلك attribute_name_index
و attribute_length
معلومات
بعد ذلك ، سوف نستخدم بنية Code
، لأنه في قيمة attribute_name_index
أشرنا إلى القيمة في مجموعة الثوابت Code
.
اقرأ المزيد: الفصل 4.7.3 سمة الرمز
هنا هو هيكلها:
Code_attribute { u2 attribute_name_index; u4 attribute_length; u2 max_stack; u2 max_locals; u4 code_length; u1 code[code_length]; u2 exception_table_length; { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count]; }
max_stack
يبدو لي أن اسم هذه السمة يمكن أن يكون مضللاً بسبب بادئة الحد الأقصى. في الواقع ، هذا هو الحد الأدنى لحجم المكدس اللازم لإكمال العملية. حسنًا ، يأخذ هذا الاسم المنطق ، ليقول الحد الأقصى لحجم المكدس الذي سيتم الوصول إليه أثناء العملية.
ببساطة ، ستقوم JVM بتخصيص مساحة لمكدس المعامل. هناك يمكنك تحديد قيمة أكبر من اللازم ، ولكن تحديد قيمة في هذه السمة أقل من اللازم سيؤدي إلى حدوث خطأ.
حول موضوع المكدس ، يمكنك قراءة " على المكدس وكومة في سياق عالم Java " أو في " JVM Internals "
max_locals
الحد الأقصى لحجم المتغيرات المحلية
يمكنك التعرف على المتغيرات المحلية إما في Mastering Java Bytecode في Core of JVM أو في نفس JVM Internals
code_length
حجم الرمز الذي سيتم تنفيذه داخل الطريقة
الكود []
كل رمز يشير إلى بعض التعليمات. يمكن العثور على جدول ارتباط optcode
والأوامر مع فن الإستذكار في قوائم تعليمات Wikipedia - Java bytecode أو في المواصفات نفسها في نهاية الكتاب
على سبيل المثال ، خذ المنشئ:
-- public Main(); 0001 --access_flags 0007 -- name_index 0008 -- descriptor_index 0001 -- attributes_count -- attribute_info 0009 -- attribute_name_index (Code) 0000 001d - attribute_length 00 01 -- max_stack 00 01 -- max_locals 00 00 00 05 -- code_length 2a b7 00 01 b1 -- code[] 0000 -- exception_table_length 0001 -- attributes_count; 00 0a -- attribute_name_index 0000 0006 -- attribute_length 00 01 00 00 00 01
هنا يمكننا العثور على الكود الخاص بنا:
2a b7 00 01 b1
نبحث عن الأوامر في الجدول ونقارن:
2a - aload_0 b7 0001 - invokespecial #1 b1 - return
كما يمكن الاطلاع على أوصاف هذه الأوامر هنا: الفصل 4.10.1.9. اكتب تعليمات الفحص
exception_table_length
يحدد عدد العناصر في response_table. ليس لدينا استثناءات لذلك لن نحللها. ولكن يمكنك أيضًا قراءة الفصل 4.7.3 سمة الرمز
استثناء
لديها الهيكل التالي:
{ u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; }
لتبسيط ، تحتاج إلى تحديد بداية ونهاية ( start_pc
، end_pc
) من الكود الذي handler_pc
catch_type
استثناء catch_type
attributes_count
عدد السمات في Code
سمات []
غالبًا ما يتم استخدام السمات بواسطة أجهزة التحليل أو مصححات الأخطاء.
أدوات Bytecode
ليس هذا هو الموضوع الذي يتعلق بهذه المقالة ، لكنه لا يزال مرتبطًا بشكل غير مباشر به.
هناك الكثير من الأدوات للعمل مع bytecode. أود هنا مراجعة مكتبة Byte Code Engineering (BCEL) من Apache Commons.
بادئ ذي بدء ، باستخدامه ، يمكننا الحصول على بعض سمات الرمز الفرعي:
قائمة رمز كامل import org.apache.bcel.classfile.*; import org.apache.commons.codec.binary.Hex; import java.io.*; public class ClassParserExample { public static void main(String... args) throws IOException, ClassNotFoundException { // read file from resources/complied/ClassA.class InputStream inputStream = ClassParserExample.class.getResourceAsStream("/compiled/ClassA.class"); if (inputStream == null) throw new FileNotFoundException(); ClassParser parser = new ClassParser(inputStream, "ClassA.class"); JavaClass clazz = parser.parse(); final String HEX_BYTECODE = getHex(clazz.getBytes()); System.out.println("Hex bytecode: "); System.out.println(HEX_BYTECODE); System.out.println(); final String MINOR_VER = getHex(clazz.getMinor()); final String MAJOR_VER = getHex(clazz.getMajor()); final String CONSTANT_POOL = getHex(clazz.getConstantPool().getConstantPool()); final String ACCESS_FLAGS = getHex(clazz.getAccessFlags()); final String THIS_CLASS = getHex(clazz.getClassName().getBytes()); final String SUPER_CLASS = getHex(clazz.getSuperClass().getBytes()); final String INTERFACES = getHex(clazz.getInterfaces()); final String FIELDS = getHex(clazz.getFields()); final String METHODS = getHex(clazz.getMethods()); final String ATTRIBUTES = getHex(clazz.getAttributes()); System.out.println( "minor: " + MINOR_VER ); // 0 System.out.println( "major: " + MAJOR_VER ); // 34 System.out.println( "constant pool: " + CONSTANT_POOL); // not correctly System.out.println( "access flags: " + ACCESS_FLAGS ); // 21 System.out.println( "this class: " + THIS_CLASS ); System.out.println( "super class: " + SUPER_CLASS ); // Object System.out.println( "interfaces: " + INTERFACES ); // <empty> System.out.println( "fields: " + FIELDS ); // <empty> System.out.println( "methods: " + METHODS ); // one method: psvm hello world System.out.println( "attributes: " + ATTRIBUTES ); // 536f7572636546696c65 - I think it's instructions for Java tools } private static String getHex(byte[] bytes){ return Hex.encodeHexString(bytes); } private static String getHex(int intNum){ return Integer.toHexString(intNum); } private static String getHex(Constant[] constants){ if (constants == null) return null; StringBuilder sb = new StringBuilder(); for (Constant c : constants){ if (c == null) continue; sb.append(getHex(c.getTag())).append(" "); } return sb.toString(); } private static String getHex(JavaClass[] clazzes){ if (clazzes == null) return null; StringBuilder sb = new StringBuilder(); for (JavaClass c : clazzes){ sb.append(getHex(c.getClassName().getBytes())).append(" "); } return sb.toString(); } private static String getHex(Field[] fields){ if (fields == null) return null; StringBuilder sb = new StringBuilder(); for (Field c : fields){ sb.append(getHex(c.getName().getBytes())).append(" "); } return sb.toString(); } private static String getHex(Method[] methods){ if (methods == null) return null; StringBuilder sb = new StringBuilder(); for (Method c : methods){ sb.append(getHex(c.getName().getBytes())).append(" "); } return sb.toString(); } private static String getHex(Attribute[] attributes){ if (attributes == null) return null; StringBuilder sb = new StringBuilder(); for (Attribute c : attributes){ sb.append(getHex(c.getName().getBytes())).append(" "); } return sb.toString(); } } /* Class A: public class ClassA { public static void main(String[] args) { System.out.println("Hello world"); } } */ /* Class A bytecode: cafe babe 0000 0034 0022 0a00 0600 1409 0015 0016 0800 170a 0018 0019 0700 1a07 001b 0100 063c 696e 6974 3e01 0003 2829 5601 0004 436f 6465 0100 0f4c 696e 654e 756d 6265 7254 6162 6c65 0100 124c 6f63 616c 5661 7269 6162 6c65 5461 626c 6501 0004 7468 6973 0100 1d4c 636f 6d2f 6170 706c 6f69 6478 7878 2f70 6172 7365 2f43 6c61 7373 413b 0100 046d 6169 6e01 0016 285b 4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b 2956 0100 0461 7267 7301 0013 5b4c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b01 000a 536f 7572 6365 4669 6c65 0100 0b43 6c61 7373 412e 6a61 7661 0c00 0700 0807 001c 0c00 1d00 1e01 000b 4865 6c6c 6f20 776f 726c 6407 001f 0c00 2000 2101 001b 636f 6d2f 6170 706c 6f69 6478 7878 2f70 6172 7365 2f43 6c61 7373 4101 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 6374 0100 106a 6176 612f 6c61 6e67 2f53 7973 7465 6d01 0003 6f75 7401 0015 4c6a 6176 612f 696f 2f50 7269 6e74 5374 7265 616d 3b01 0013 6a61 7661 2f69 6f2f 5072 696e 7453 7472 6561 6d01 0007 7072 696e 746c 6e01 0015 284c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b29 5600 2100 0500 0600 0000 0000 0200 0100 0700 0800 0100 0900 0000 2f00 0100 0100 0000 052a b700 01b1 0000 0002 000a 0000 0006 0001 0000 0006 000b 0000 000c 0001 0000 0005 000c 000d 0000 0009 000e 000f 0001 0009 0000 0037 0002 0001 0000 0009 b200 0212 03b6 0004 b100 0000 0200 0a00 0000 0a00 0200 0000 0800 0800 0900 0b00 0000 0c00 0100 0000 0900 1000 1100 0000 0100 1200 0000 0200 13 */ /* Assembled code: Classfile /C:/java/BCEL/src/main/resources/compiled/ClassA.class Last modified 08.12.2019; size 563 bytes MD5 checksum bcd0198f6764a1dc2f3967fef701452e Compiled from "ClassA.java" public class com.apploidxxx.parse.ClassA minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#20 // java/lang/Object."<init>":()V #2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #23 // Hello world #4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #26 // com/apploidxxx/parse/ClassA #6 = Class #27 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lcom/apploidxxx/parse/ClassA; #14 = Utf8 main #15 = Utf8 ([Ljava/lang/String;)V #16 = Utf8 args #17 = Utf8 [Ljava/lang/String; #18 = Utf8 SourceFile #19 = Utf8 ClassA.java #20 = NameAndType #7:#8 // "<init>":()V #21 = Class #28 // java/lang/System #22 = NameAndType #29:#30 // out:Ljava/io/PrintStream; #23 = Utf8 Hello world #24 = Class #31 // java/io/PrintStream #25 = NameAndType #32:#33 // println:(Ljava/lang/String;)V #26 = Utf8 com/apploidxxx/parse/ClassA #27 = Utf8 java/lang/Object #28 = Utf8 java/lang/System #29 = Utf8 out #30 = Utf8 Ljava/io/PrintStream; #31 = Utf8 java/io/PrintStream #32 = Utf8 println #33 = Utf8 (Ljava/lang/String;)V { public com.apploidxxx.parse.ClassA(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/apploidxxx/parse/ClassA; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello world 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 8: 0 line 9: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String; } SourceFile: "ClassA.java */
, (, Jasmin) -.
, Jasmin . , , , JVM -.
:
Hello World .bytecode 52.0 .source Main.j .class public Main .super java/lang/Object .method public static main([Ljava/lang/String;)V .limit stack 2 .limit locals 2 getstatic java/lang/System/out Ljava/io/PrintStream; ldc "Hello world!" invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V return .end method
; ClassCreating.j .bytecode 52.0 .source ClassCreating.java .class public ClassCreating .super java/lang/Object .method public <init>()V .limit stack 1 .limit locals 1 .line 1 0: aload_0 1: invokespecial java/lang/Object/<init>()V 4: return .end method .method public static main([Ljava/lang/String;)V ; Flag ACC_VARARGS set, see JVM spec .limit stack 2 .limit locals 3 .line 3 0: new java/lang/String 3: dup 4: invokespecial java/lang/String/<init>()V 7: astore_1 .line 4 8: new ClassCreating 11: dup 12: invokespecial ClassCreating/<init>()V 15: astore_2 .line 5 16: aload_2 17: invokevirtual ClassCreating/sayHello()V .line 6 20: return .end method .method public sayHello()V .limit stack 2 .limit locals 1 .line 9 0: getstatic java/lang/System/out Ljava/io/PrintStream; 3: ldc "Hello, User!" 5: invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V .line 10 8: return .end method
Hello World
- : gist.github
.
الأدب المستخدم