جافا REPL كنت لا ScriptEngine



مرحبا يا هبر! اسمي ديما ، أنا مطور في فريق "الهندسة المعمارية" في hh.ru. من بين أشياء أخرى ، أقوم بتسهيل الأمور على الزملاء. تشغيل التعليمات البرمجية في الإنتاج مهمة نموذجية. لذلك ، عندما سمعت أن هناك مشاكل في هذا ، قررت إصلاحها.

ليس من الممكن دائمًا إجراء تغييرات بسيطة على البيانات / إدراج - في بعض الأحيان تحتاج إلى استخدام التحقق من الصحة وحافلات الأحداث وأكثر من ذلك. في مثل هذه الحالات ، فإن الحل الأمثل هو تنفيذ تعليمات برمجية عشوائية مباشرة في التطبيق. لدينا جافا ، لذلك عندما ظهر JEP-222 ، اعتقدت على الفور أن JShell قد تكون قادرة على جعل البرمجة النصية مريحة مرة أخرى. لم تحدث المعجزة ، وبالتالي ستجد في المقاطع مقارنة غير عميقة بين أشهر مترجمي جافا (و "بالقرب من جافا") لـ jvm مع أمثلة. أدعو كل المهتمين تحت القط.

نحن نستخدم BeanShell لتشغيل البرامج النصية ، وهو أمر فظيع لعام 2019: الإصدار الأخير من عام 2016 ، عدم وجود دعم لل lambdas وحتى الأدوية الجنيسة - كل هذا يجبرنا على كتابة رمز لم يكتبه أحد منذ Java 1.4 .

معايير



قبل البدء في المقارنة ، نقوم بصياغة متطلبات محرك البرمجة النصية المضمّن. بعد خدش رأسي ، قمت بتكوين القائمة التالية:

  1. دعم بناء جملة جافا الحالي ؛
  2. القدرة على تمرير سياق خارجي للمترجم الفوري ؛
  3. القدرة على مقاطعة التنفيذ ؛
  4. القدرة على إعادة توجيه I / O ؛
  5. ردود الفعل بالمعلومات.

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

لذلك دعونا نذهب

JShell



  • دعم بناء جملة جافا الحالي - نعم
  • القدرة على نقل السياق - لا
  • القدرة على مقاطعة التنفيذ - نعم
  • القدرة على إعادة توجيه I / O - لا
  • ردود الفعل بالمعلومات - نعم

يجب أن أقول على الفور أن JEP-222 لا يهدف إلى إنشاء مترجم مضمن - هدفه هو REPL ، أي القدرة على كتابة نموذج أولي بسرعة. هذا محفوف بعدد من العواقب.

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

ثانياً ، قد يتم تنفيذ REPL ليس محليًا ، ولكن في مكان ما على جهاز بعيد ، لذلك يتم إنشاء واجهة برمجة التطبيقات مع وضع هذه الميزات في الاعتبار. أعتقد أن هذا هو السبب الرئيسي لعدم تمكن API من تمرير سياق خارجي إلى المترجم وإعادة توجيه الإدخال / الإخراج.
بالإضافة إلى ذلك ، هناك أوضاع تشغيل مختلفة - عن بعد ، عندما تتصل الصدفة بالجهاز عبر JDI والمحلية . نظرًا لعدم وجود إمكانية لنقل السياق برمجيًا ، لكننا لا نزال نريد حقًا ، يبقى الأمل في الوضع المحلي فقط وأن نتمكن من استخدام توليد الشفرة
ولكن ، لسوء الحظ ، من الواضح أن الوضع المحلي لم يُصوَّر على أنه الوضع الرئيسي - وهذا النوع من النصوص يدعو إلى طريق مسدود في برنامج التحويل البرمجي. على الرغم من حقيقة أن نفس الرمز في وضع JDI يعمل دون مشاكل.

وبالتالي ، كان علي أن أتخلى عن استخدام JShell ، على الرغم من أن واجهة برمجة التطبيقات بشكل عام غريبة ، لكنها مفهومة - نحن نمنح النص البرمجي للمدخلات ، ونحصل على مجموعة الأحداث ، ولكل واحد منهم يمكننا التحقق من الحالة ، والحصول على الأخطاء ومعلومات الفوترة. تسمح لنا الأخطاء بتحديد التعبير الذي تم السماح به فيه:



استكمال: جمعت ScriptEngine من JShell . ولكن لهذا اضطررت إلى كتابة وضع الإطلاق الخاص بي

بيانشيل



  • دعم بناء جملة جافا الحالي - لا
  • القدرة على نقل السياق - نعم
  • القدرة على مقاطعة التنفيذ - نعم
  • القدرة على إعادة توجيه الإدخال / الإخراج - نعم (ولكن يتطلب استخدام أساليب خاصة)
  • ردود الفعل بالمعلومات - نعم

جعلنا الفشل نلاحظ ما نستخدمه الآن. بعد فترة استراحة طويلة ، بدا أن المشروع قد بدأ في العمل ، واستنادا إلى خريطة الطريق ، فإنه يتحرك بثقة نحو إصدار سيحل جميع مشاكلنا - يجب أن تعمل الكثير من الميزات الآن.

في وقت كتابة هذا التقرير ، كان الفاصولياء يدعم بالفعل الأدوية الجنيسة ، لكن لا تزال لمبات لا تعمل. ربما من خلال الافراج عن الوضع سوف يتغير الوضع.
ولكن فيما يتعلق بالتكامل ، يكون المحرك ودودًا للغاية - دعم javax.scripting القياسي ، أخطاء وقت التشغيل مطلقة بما فيه الكفاية:



ومع ذلك ، فإن استخدام تدفقات خالية من لامدا هو الجحيم الذي يحترق في الجحيم. قد يكون من الأسهل الكتابة بلغة أخرى. لذلك قررت إلقاء نظرة فاحصة على الجزء "القريب من جافا". والمرشح الأول لدور مترجم سيناريو هنا ، بالطبع

Kotlin


  • دعم بناء جملة جافا الحالي - لا
  • القدرة على نقل السياق - لا
  • القدرة على مقاطعة التنفيذ - نعم
  • القدرة على إعادة توجيه I / O - لا
  • ردود الفعل بالمعلومات - نعم

كود جافا ، مع الكثير من الحظ ، سيكون رمز kotlin صالح. لكنني لم تنجح في إطلاق أي شيء على الأقل بطريقة كافية على جافا في kotlin ، ولكن مع ذلك دعونا نجرب.
أعلنت Kotlin بالفعل دعم javax.scripting لبضع سنوات.

المشكلة الأولى التي يتعين علينا التعامل معها هي الجحيم التبعية.
يتضمن Kotlin-compiler فصول org.jdom التي بدأت في محاربة org.jdom في التطبيق ولفها ... لذلك ، لدينا kotlin-compiler-embeddable ، حيث يتم وضع كل هذه الفئات في حزم مخصصة.

ومع ذلك ، بعد التكوين ، اتضح أن نقل السياق الخارجي لا يعمل. والآن هذه مشكلة خطيرة ، وحتى حلها ، لا معنى للحفر بشكل أعمق. إذا كنت تعرف المشكلة الموجودة وكيفية حلها - اكتب التعليقات.

الأخطاء هي أيضًا مطوّل تمامًا:



رائع



  • دعم بناء جملة جافا الحالي - لا ، ولكن هناك نظائرها
  • القدرة على نقل السياق - نعم
  • القدرة على مقاطعة التنفيذ - نعم
  • القدرة على إعادة توجيه I / O - نعم
  • ردود الفعل بالمعلومات - نعم

توفر الأخاديد ، بالإضافة إلى دعم javax.scripting ، واجهة برمجة تطبيقات خاصة بها وأكثر تقدمًا لتكامل المترجم الفوري. على سبيل المثال ، من الممكن تمرير تحويل AST ، والذي يسمح لك بإضافة مقاطعة مشروطة بعد كل تعبير . الأمر قوي لدرجة أنه مخيف بالفعل.

علاوة على ذلك ، يمكن أن يكون رمز Java (وخاصة beanshell) رمزًا صحيحًا تمامًا للأخدود.
لقد نجحت عملية الدمج والاختبار ، باستثناء تهيئة الورقة وبناء جملة lambda (يجب أن تكون ملفوفة بأقواس مجعد) ، عملت البرامج النصية الموجودة في حبيبات الورق دون مشاكل. الأخطاء أكثر من مطول:



ربما اليوم هو المترجم الوحيد الذي يسمح لك ، من ناحية ، بكتابة رمز للعينة في 2019 ، ومن ناحية أخرى ، يلبي جميع المتطلبات التي من المعقول تقديمها إلى المترجم الشفهي.

ما هي الاستنتاجات التي يمكن أن نستخلصها؟


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

الثاني - اليوم لا توجد أداة واحدة لتنفيذ البرامج النصية في Java ، لذلك إذا كنت بحاجة إلى مثل هذه الأداة ، فاستعد لتعلم بناء جملة جديد.

Source: https://habr.com/ru/post/ar461027/


All Articles