هل يمكن لملفات فئة Java استخدام الكلمات الأساسية المحجوزة كأسماء؟




reflection jvm (3)

أدرك أن لغة برمجة Java-the-compilable-language ليست واحدة كما هي Java-the-bytecode-format-for-JVM. هناك أمثلة لأشياء صالحة في تنسيق .class ولكن ليس في التعليمات البرمجية المصدر .java ، مثل الفئات الخالية من المنشئ والأساليب الاصطناعية.

  1. إذا قمنا بصياغة ملف .class يدويًا باستخدام كلمة أساسية محجوزة بلغة Java (على سبيل المثال int ، while ) كصنف أو طريقة أو اسم حقل ، فهل يقبل الجهاز الظاهري Java تحميله؟

  2. إذا تم تحميل الفصل ، فهل يعني ذلك أن الطريقة الوحيدة للوصول إلى هذه الفئة أو العضو هي من خلال انعكاس Java ، لأن الاسم غير قانوني من الناحية اللغوية في لغة برمجة Java؟


  1. الكلمات الأساسية معروفة فقط للمترجم. يقوم المترجم بترجمتها إلى bytecode مناسب. لذلك لا وجود لها أثناء وقت تشغيل الكود البرمجي المترجم ، وبالتالي ، لا يتم التحقق منه بواسطة JVM.
  2. بالتأكيد ، لا يمكنك الوصول إلى أعضاء الفصل غير المعروفين في وقت الترجمة. ولكن يمكنك استخدام الانعكاس لهذا الغرض إذا كنت متأكدًا من وجود عضو في الفصل في التعليمات البرمجية المترجمة (ستقوم "بصنعها يدويًا" هناك) ، لأن المترجم لا يتم التحقق من الوصول بواسطة الانعكاس.

القيود الوحيدة على أسماء الفئات على مستوى الرمز الثانوي هي أنها لا يمكن أن تحتوي على الأحرف [ ، . أو وأنهم على الأقل 65535 بايت. من بين أشياء أخرى ، هذا يعني أنه يمكنك استخدام الكلمات المحجوزة أو مسافة بيضاء أو أحرف خاصة أو Unicode أو حتى أشياء غريبة مثل الخطوط الجديدة بحرية.

يمكنك نظريًا استخدام الأحرف الفارغة في اسم الفصل ، ولكن نظرًا لأنه من المستحيل وجود حرف فارغ في اسم الملف ، لا يمكنك تضمين مثل هذا classfile في برطمان. قد تتمكن من إنشاء وتحميل واحد بشكل حيوي.

فيما يلي مثال لبعض الأشياء التي يمكنك القيام بها (مكتوبة في تجميع Krakatau):

; Entry point for the jar
.class Main
.super java/lang/Object

.method public static main : ([Ljava/lang/String;)V
    .limit stack 10
    .limit locals 10
    invokestatic int                                hello ()V
    invokestatic "-42"                              hello ()V
    invokestatic ""                                 hello ()V
    invokestatic "  some  whitespace and \t tabs"   hello ()V
    invokestatic "new\nline"                        hello ()V
    invokestatic 'name with "Quotes" in it'         hello ()V
    return
.end method
.end class


.class int
.super java/lang/Object
.method public static hello : ()V
    .limit stack 2
    .limit locals 0
    getstatic java/lang/System out Ljava/io/PrintStream;
    ldc "Hello from int"
    invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V
    return
.end method
.end class

.class "-42"
.super java/lang/Object
.method public static hello : ()V
    .limit stack 2
    .limit locals 0
    getstatic java/lang/System out Ljava/io/PrintStream;
    ldc "Hello from -42"
    invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V
    return
.end method
.end class

; Even the empty string can be a class name!
.class ""
.super java/lang/Object
.method public static hello : ()V
    .limit stack 2
    .limit locals 0
    getstatic java/lang/System out Ljava/io/PrintStream;
    ldc "Hello from "
    invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V
    return
.end method
.end class

.class "  some  whitespace and \t tabs"
.super java/lang/Object
.method public static hello : ()V
    .limit stack 2
    .limit locals 0
    getstatic java/lang/System out Ljava/io/PrintStream;
    ldc "Hello from   some  whitespace and \t tabs"
    invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V
    return
.end method
.end class

.class "new\nline"
.super java/lang/Object
.method public static hello : ()V
    .limit stack 2
    .limit locals 0
    getstatic java/lang/System out Ljava/io/PrintStream;
    ldc "Hello from new\nline"
    invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V
    return
.end method
.end class

.class 'name with "Quotes" in it'
.super java/lang/Object
.method public static hello : ()V
    .limit stack 2
    .limit locals 0
    getstatic java/lang/System out Ljava/io/PrintStream;
    ldc "Hello from name with \"Quotes\" in it"
    invokevirtual java/io/PrintStream println (Ljava/lang/Object;)V
    return
.end method
.end class

خرج التنفيذ:

Hello from int
Hello from -42
Hello from
Hello from   some  whitespace and        tabs
Hello from new
line
Hello from name with "Quotes" in it

راجع إجابة Holger لمعرفة السعر الدقيق للقواعد من مواصفات JVM.


يتم إصلاح القيود على الأسماء في مواصفات JVM:

§4.2.1. أسماء فئة ثنائية وواجهة

يتم دائمًا تمثيل أسماء الفئات والواجهات التي تظهر في بنيات ملفات الفصل في نموذج مؤهل تمامًا يُعرف باسم الأسماء الثنائية (JLS § 13.1). يتم دومًا تمثيل هذه الأسماء على أنها بنية CONSTANT_Utf8_info (الفقرة 4.4.7) ، وبالتالي يمكن استخلاصها ، من حيث لا يتم تقييدها ، من مساحة الشفرة Unicode بأكملها ...

لأسباب تاريخية ، يختلف بناء جملة الأسماء الثنائية التي تظهر في بنيات ملفات الفصل الدراسي عن بناء جملة الأسماء الثنائية الموثقة في JLS §13.1. في هذا النموذج الداخلي ، يتم استبدال فترات ASCII ( . ) التي تفصل عادةً معرفات الهوية التي تشكل الاسم الثنائي بخط مائل ASCII للأمام ( / ). يجب أن تكون المعرفات نفسها أسماء غير مؤهلة (الفقرة 2.4.2).

§4.2.2. أسماء غير مؤهلة

يتم تخزين أسماء الطرق والحقول والمتغيرات المحلية والمعلمات الرسمية كأسماء غير مؤهلة. يجب أن يحتوي الاسم غير المؤهل على نقطة واحدة على الأقل لرمز Unicode ويجب ألا يحتوي على أي من أحرف ASCII . ; [ / (أي ، فترة أو فاصلة منقوطة أو قوس مربع الأيسر أو شرطة مائلة للأمام).

يتم تقييد أسماء الطرق بشكل أكبر ، باستثناء أسماء الطرق الخاصة <init> و <clinit> (§2.9) ، يجب ألا تحتوي على أحرف ASCII < أو > (أي ، قوس الزاوية اليسرى أو قوس الزاوية اليمنى) .

إذن الجواب هو ، هناك فقط عدد قليل من الشخصيات لا يمكنك استخدامها على المستوى الثنائي. أولاً ، / هو فاصل الحزمة. ثم ، و [ لا يمكن استخدامها لأن لها معنى خاص في توقيعات المجال وتوقيعات الطريقة التي قد تحتوي على أسماء كتابة. في هذه التوقيعات ، [ يبدأ نوع صفيف و ; يصادف نهاية اسم نوع المرجع.

لا يوجد سبب واضح لماذا . ممنوع. لا يتم استخدامه داخل JVM وليس له سوى معنى ضمن التواقيع العامة ولكن إذا كنت تستخدم التواقيع العامة ، فسيتم تقييد أسماء الأنواع بشكل أكبر بعدم السماح بتضمين < ، > ، : بالإضافة إلى أن هذه الأحرف لها معنى خاص داخل التواقيع العامة أيضا.

وبالتالي ، انتهاك المواصفات باستخدام . ضمن معرفات ليس له تأثير على الوظيفة الأساسية لل JVM. هناك عتامات تفعل ذلك. تعمل التعليمة البرمجية الناتجة ولكن قد تواجه مشكلات مع Reflection عند السؤال عن توقيعات النوع العام. أيضًا ، تحويل الأسماء الثنائية إلى اسم مصدر عن طريق استبدال الكل / s بـ . ستصبح s لا رجعة فيها إذا كان الاسم الثنائي يحتوي على . س.

قد يكون من المثير للاهتمام أنه كان هناك اقتراح لدعم جميع المعرّفات الممكنة ضمن بناء جملة Java (انظر النقطة 3 ، "المعرّفات الغريبة") ، لكنه لم يصل إلى Java 7. النهائي ويبدو أنه لا يوجد أحد حاليًا محاولة جديدة لإحضارها.

يوجد قيود فنية إضافية على أن الأسماء لا يمكن أن يكون لها تمثيل UTF-8 معدل أطول من 65535 بايت لأنه يتم تخزين عدد البايتات كقيمة قصيرة غير موقعة.





.class-file