
تم إنشاء JavaScript Engine Switcher في الأصل كمكتبة مساعدة وتحدد تطويرها إلى حد كبير باحتياجات المكتبات التي تستخدمها. في الواقع ، قام كل إصدار من إصداراته الرئيسية بحل واحد أو عدة مهام رئيسية ضرورية لزيادة تطوير المكتبات الأخرى:
- في الإصدار الأول ، كانت هذه المهمة لإضافة أكبر عدد ممكن من وحدات المحول لمحركات JS الشهيرة التي تدعم نظام .NET. وقد أعطى ذلك لمستخدمي Bundle Transformer بعض المرونة: على أجهزة الكمبيوتر الخاصة بالمطور ، يمكنهم استخدام وحدة MSIE التي تدعم تصحيح تعليمات برمجية JS باستخدام Visual Studio ، وعلى الخوادم التي ليس لديها إصدار حديث من Internet Explorer أو لم يتم تثبيتها على الإطلاق ، يمكنهم استخدام وحدة V8 . تمكن البعض من تشغيل Bundle Transformer في Mono على Linux و Mac باستخدام وحدات Jurassic و Jint .
- كانت المهمة الرئيسية للنسخة الثانية هي تنفيذ دعم .NET Core ، والذي كان مطلوبًا للإصدار الجديد من مكتبة ReactJS.NET . كانت المهمة الهامة الأخرى هي إنشاء وحدة نمطية متعددة المنصات يمكنها معالجة كميات كبيرة من كود JS بسرعة (لم تكن وحدات Jurassic و Jint مناسبة لهذا) ، وبعد عدد من التحسينات ، أصبحت وحدة ChakraCore هذه الوحدة.
- في الإصدار الثالث ، كان التركيز الرئيسي على تحسين التكامل مع مكتبة ReactJS.NET وتحسين الإنتاجية.
في هذه المقالة ، سننظر في بعض ابتكارات الإصدار الثالث ، والتي تبين أن الكثير منها غير واضح حتى بعد قراءة نص الإصدار وقسم الوثائق "كيفية ترقية التطبيقات إلى الإصدار 3.X" : التغييرات في فئة JsEngineSwitcher
، وإعادة تنظيم الاستثناءات ، والمزيد من رسائل الخطأ المفيدة ، المقاطعة وتجميع أولي للبرامج النصية ، القدرة على تغيير الحد الأقصى لحجم المكدس في وحدات ChakraCore و MSIE ، بالإضافة إلى وحدة نمطية جديدة تستند إلى NiL.JS.
التغييرات في JsEngineSwitcher Class
في الإصدار الجديد ، JsEngineSwitcher
فئة IJsEngineSwitcher
واجهة IJsEngineSwitcher
ولم تعد مفردة (يمكنك إنشاء مثيل لها باستخدام المشغل new
). للحصول على مثيل عمومي ، بدلاً من الخاصية Instance
، استخدم الخاصية Current
. تحتوي الخاصية Current
، بخلاف خاصية Instance
المهملة ، على نوع الإرجاع IJsEngineSwitcher
. تحتوي الخاصية Current
أيضًا على أداة ضبط ، والتي يمكنك من خلالها استبدال التطبيق القياسي بالتطبيق الخاص بك:
JsEngineSwitcher.Current = new MyJsEngineSwitcher();
في تطبيقات الويب ASP.NET Core حيث تم تثبيت JavaScriptEngineSwitcher.Extensions.MsDependencyInjection ، يتم استبدال التطبيق باستخدام AddJsEngineSwitcher
ملحق AddJsEngineSwitcher
:
… using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; … public class Startup { … public void ConfigureServices(IServiceCollection services) { … services.AddJsEngineSwitcher(new MyJsEngineSwitcher(), options => … ) … ; … } … } …
تعمل هذه التغييرات دائمًا على "كسر" التطبيقات أو المكتبات التي تستخدم الإصدار السابق من JavaScript Engine Switcher. لذلك ، تحتاج إلى إجراء التغييرات التالية على التعليمات البرمجية الخاصة بك:
- تغيير نوع المتغيرات أو المعلمات أو الخصائص من
JsEngineSwitcher
إلى IJsEngineSwitcher
. - بدلاً من الخاصية
Instance
، استخدم الخاصية Current
كل مكان.
تجدر الإشارة إلى أنه بالنسبة لمعظم المطورين ، لن تكون هذه التغييرات ذات فائدة خاصة ، لأن هدفهم الرئيسي كان تبسيط اختبارات الوحدات (على سبيل المثال ، في السابق ، في اختبارات الوحدة في مكتبة ReactJS.NET ، تم استخدام الأقفال ، ولكن الآن يمكنك الاستغناء عنها ).
إعادة تنظيم الاستثناءات
قبل الإصدار الثالث ، تحولت معظم أخطاء مشغل JS إلى JsRuntimeException
نوع JsRuntimeException
، فقط الأخطاء التي حدثت أثناء عملية تهيئة المحرك تحولت إلى JsEngineLoadException
. كان هناك أيضًا فئة JsException
الفئة الأساسية ، والتي تم من خلالها توريث نوعين من الاستثناءات المذكورة أعلاه ، مما مكن من اعتراض جميع الأخطاء التي حدثت أثناء تشغيل محركات JS. على الرغم من العيوب الواضحة ، تتوافق مجموعة الاستثناءات هذه بشكل جيد مع مفهوم الواجهة الموحدة للوصول إلى القدرات الأساسية لمحركات JS.
ولكن مع تنفيذ المقاطعة والتجميع المسبق للبرامج النصية (سأناقشها في الأقسام التالية) ، نشأت حاجة إلى مقاربة جديدة لتنظيم الاستثناءات. أول شيء فعله هو إضافة نوع جديد من الاستثناء - JsInterruptedException
، والذي كان ضروريًا لإعلام المستخدم عن انقطاع تنفيذ البرنامج النصي. ثم كان من الضروري تقسيم جميع الأخطاء التي حدثت أثناء معالجة البرنامج النصي بشكل صريح إلى مجموعتين: أخطاء الترجمة (تحليل) وأخطاء وقت التشغيل. كان مطلوبًا أيضًا فصل جميع أنواع الأخطاء المحددة الخاصة بـ Chakra و V8 ، والتي لا تتعلق بمعالجة النصوص. كان من الضروري أيضًا مراعاة وجود استثناء في محرك Jint يحدث عند انقضاء فترة المهلة لتنفيذ برنامج نصي (مهلة). ونتيجة لذلك ، تم تشكيل نهج جديد لتنظيم الاستثناءات ، والذي يمكن تمثيله على أنه الهيكل الهرمي التالي:
JsException
JsEngineException
JsFatalException
JsScriptException
JsCompilationException
JsRuntimeException
JsInterruptedException
JsTimeoutException
JsUsageException
JsEngineNotFoundException
*
* - لا يحدث هذا الاستثناء على مستوى محرك JS ، ولكن على مستوى JavaScript Engine Switcher.
أعتقد أن التسلسل الهرمي للاستثناءات المذكورة أعلاه لا يحتاج إلى تعليقات ، لأن أسماء الاستثناءات تتحدث عن نفسها. باستخدام هذا النهج ، لا نحصل على مزيد من المعلومات حول أسباب الخطأ فحسب ، بل يمكننا أيضًا معالجة أنواع معينة من الاستثناءات بمرونة أكبر.
تنسيق رسالة خطأ موحدة
هناك مشكلة أخرى في الإصدارات السابقة من JavaScript Engine Switcher وهي صعوبة تحديد موقع الأخطاء التي حدثت أثناء معالجة البرامج النصية. من خاصية استثناء Message
، كان من الصعب أن نفهم بالضبط أين حدث الخطأ في الكود ، لذلك اضطررت إلى تحليل خصائص الاستثناء الأخرى ، والتي لم تكن مريحة دائمًا. بالإضافة إلى ذلك ، كانت مجموعة الخصائص الموجودة أيضًا غير كافية.
لذلك ، تمت إضافة JsScriptException
فئة JsScriptException
:
- النوع - نوع خطأ JavaScript (على سبيل المثال ،
SyntaxError
أو SyntaxError
) ؛ - DocumentName - اسم المستند (عادةً ما يتم استخراجه من قيم المعلمات التالية:
documentName
من أساليب Execute
ExecuteFile
، ExecuteFile
طريقة ExecuteFile
، و resourceName
للأسلوب ExecuteResource
، إلخ) ؛
تمت إضافة خاصية جديدة واحدة أيضًا إلى فئة JsRuntimeException - CallStack ، والتي تحتوي على تمثيل سلسلة مكدس الاستدعاءات.
سابقًا ، تم ببساطة نسخ قيمة من خاصية مشابهة للاستثناء .NET الأصلي أو تمثيل سلسلة لخطأ JavaScript إلى خاصية Message
. في كثير من الأحيان ، تختلف رسائل الخطأ في محركات JS المختلفة ليس فقط في التنسيق ، ولكن أيضًا في مقدار المعلومات المفيدة المقدمة فيها. على سبيل المثال ، نظرًا لنقص المعلومات حول أرقام الصفوف والأعمدة في بعض رسائل الخطأ ، اضطر مطورو مكتبة ReactJS.NET إلى العودة إلى الاستثناءات التي تم تلقيها من JavaScript Engine Switcher.
لذلك ، قررت إنشاء رسائل الخطأ الخاصة بي على مستوى وحدة المحول ، والتي سيكون لها تنسيق واحد (موحد). يستخدم هذا التنسيق جميع معلومات الخطأ المتاحة: النوع والوصف واسم المستند ورقم السطر ورقم العمود وجزء الكود ومكدس الاتصال. كأساس للتنسيق الجديد ، أخذت تنسيق الخطأ من مكتبة Microsoft ClearScript .
فيما يلي الرسائل حول خطأ التحويل البرمجي نفسه الذي تم إنشاؤه بواسطة وحدات محول مختلفة:
ChakraCore ========== SyntaxError: Unexpected identifier after numeric literal at declinationOfSeconds.js:12:23 -> caseIndex = number % 1O < 5 ? number % 10 : 5; Jint ==== SyntaxError: Unexpected token ILLEGAL at declinationOfSeconds.js:12:25 Jurassic ======== SyntaxError: Expected operator but found 'O' at declinationOfSeconds.js:12 MSIE Classic ===================== SyntaxError: Expected ';' at declinationOfSeconds.js:12:25 -> caseIndex = number % 1O < 5 ? number % 10 : 5; MSIE Chakra ActiveScript ================================= SyntaxError: Expected ';' at declinationOfSeconds.js:12:25 -> caseIndex = number % 1O < 5 ? number % 10 : 5; MSIE Chakra IE JsRT ============================ SyntaxError: Expected ';' at 12:25 -> caseIndex = number % 1O < 5 ? number % 10 : 5; MSIE Chakra Edge JsRT ============================== SyntaxError: Unexpected identifier after numeric literal at declinationOfSeconds.js:12:23 -> caseIndex = number % 1O < 5 ? number % 10 : 5; NiL === SyntaxError: Unexpected token 'O' at 12:25 V8 == SyntaxError: Invalid or unexpected token at declinationOfSeconds.js:12:24 -> caseIndex = number % 1O < 5 ? number % 10 : 5; Vroom ===== SyntaxError: Unexpected token ILLEGAL at declinationOfSeconds.js:12:24
مثال مشابه لخطأ وقت التشغيل:
ChakraCore ========== TypeError: Unable to get property '' of undefined or null reference at transliterate (russian-translit.js:929:4) -> newCharValue = typeof charMapping[charValue] !== 'undefined' ? at Global code (Script Document:1:1) Jint ==== TypeError: charMapping is undefined at russian-translit.js:929:26 Jurassic ======== TypeError: undefined cannot be converted to an object at transliterate (russian-translit.js:929) at Global code (Script Document:1) MSIE Classic ===================== TypeError: 'undefined' is null or not an object at russian-translit.js:929:4 MSIE Chakra ActiveScript ================================= TypeError: Unable to get property '' of undefined or null reference at russian-translit.js:929:4 MSIE Chakra IE JsRT ============================ TypeError: Unable to get property '' of undefined or null reference at transliterate (russian-translit.js:929:4) at Global code (Script Document:1:1) MSIE Chakra Edge JsRT ============================== TypeError: Unable to get property '' of undefined or null reference at transliterate (russian-translit.js:929:4) at Global code (Script Document:1:1) NiL === TypeError: Can't get property "" of "undefined" V8 == TypeError: Cannot read property '' of undefined at transliterate (russian-translit.js:929:37) -> newCharValue = typeof charMapping[charValue] !== 'undefined' ? at Script Document:1:1 Vroom ===== TypeError: Cannot read property '' of undefined at russian-translit.js:929:37
كما ترون من الأمثلة ، توفر لنا بعض محركات JS وصفًا مختلفًا تمامًا للأخطاء وأرقام الأعمدة ، ولا يمكننا دائمًا الحصول على مجموعة كاملة من بيانات الأخطاء ، ولكن على الرغم من أوجه القصور هذه ، فإن التنسيق الموحد يعطينا المزيد من المعلومات حول موقع الخطأ من رسائل الخطأ الأصلية.
نصائح نشر التجمع الأصلي
السبب الرئيسي للأخطاء عند العمل مع الإصدار الثاني من JavaScript Engine Switcher هو أن العديد من المطورين نسوا تثبيت حزم NuGet التي تحتوي على تجميعات أصلية لوحدات ChakraCore و V8. في وقت واحد ، تم تخصيص منشور في ReactJS.NET bugtracker أيضًا لهذه المشكلة ( الترجمة الروسية متوفرة أيضًا). الآن ، يتم مواجهة مثل هذا الخطأ بشكل أساسي فقط من قبل المبتدئين ، الذين لم يقرأوا الوثائق لسبب ما.
حاول مؤلفو ReactJS.NET تقليل عدد هذه الأخطاء باستخدام التلميحات الموجودة داخل رسائل الخطأ ، لكن التنفيذ غير الناجح لهذا النهج أدى إلى مزيد من الالتباس . كانت فكرة التلميحات جيدة ، ولكنها تتطلب تطبيقًا مختلفًا بشكل أساسي ، أي التنفيذ على مستوى وحدات المحول لمحركات JS. في الإصدار الجديد من JavaScript Engine Switcher ، تتم إضافة هذه التلميحات إلى رسالة الخطأ عند التفاف DllNotFoundException
و TypeLoadException
JsEngineLoadException
(انظر أمثلة التنفيذ لوحدات ChakraCore و V8 و Vroom ). علاوة على ذلك ، هذه النصائح ذكية ، لأنه يأخذ جيلهم في الاعتبار عددًا من العوامل: نوع نظام التشغيل ، بنية المعالج ، ووقت التشغيل (.NET Framework ، .NET Core أو Mono).
على سبيل المثال ، عند استخدام وحدة ChakraCore بدون تجميع أصلي في عملية 64 بت على نظام التشغيل Windows ، ستبدو رسالة الخطأ كما يلي:
فشل في تكوين نسخة من ChakraCoreJsEngine. على الأرجح حدث ذلك ، لأنه لم يتم العثور على التجميع 'ChakraCore.dll' أو أحد تبعياته. حاول تثبيت حزمة JavaScriptEngineSwitcher.ChakraCore.Native.win-x64 عبر NuGet. بالإضافة إلى ذلك ، لا تزال بحاجة إلى تثبيت Microsoft Visual C ++ Redistributable لـ Visual Studio 2017 ( https://www.visualstudio.com/downloads/#microsoft-visual-c-redistributable-for-visual-studio-2017 ).
توضح رسالة الخطأ تلميحًا إلى أنك تحتاج إلى تثبيت حزمة جافا سكريبت NuGetEngineSwitcher.ChakraCore.Native.win-x64 ، كما تذكر أن ChakraCore for Windows يتطلب مكون Microsoft Visual C ++ القابل لإعادة التوزيع لـ Visual Studio 2017 للعمل. يحدث في عملية 32 بت ، سيُطلب من المستخدم تثبيت حزمة JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.
تبدو رسالة خطأ مشابهة على نظام Linux في .NET Core كما يلي:
فشل في تكوين نسخة من ChakraCoreJsEngine. على الأرجح حدث ذلك ، لأنه لم يتم العثور على مجموعة libChakraCore.so أو أحد تبعياتها. حاول تثبيت حزمة JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 عبر NuGet.
في هذه الحالة ، سيتم اقتراح تثبيت حزمة JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64.
عند تشغيله في Mono ، سيتم عرض مطالبة أخرى:
... حزم JavaScriptEngineSwitcher.ChakraCore.Native.linux- * لا تدعم التثبيت تحت Mono ، لكن يمكنك تثبيت التجميع الأصلي يدويًا ( https://github.com/Taritsyn/JavaScriptEngineSwitcher/wiki/ChakraCore#linux ).
نظرًا لأن حزمة JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 متوافقة فقط مع .NET Core ، فإن تلميح الأدوات سوف يوفر رابطًا لتعليمات نشر التجميع الأصلي يدويًا على Linux.
يمكن إعطاء العديد من الأمثلة ، لكن هذا لا معنى له.
إحباط تنفيذ البرنامج النصي
عندما نعطي المستخدمين الفرصة لتنفيذ كود JS التعسفي على الخادم ، فإننا نواجه مشكلة واحدة خطيرة للغاية - لا ندري كم من الوقت سيستغرق تنفيذ هذا الرمز. قد يكون هذا مقدارًا كبيرًا من الكود أو الكود غير الأمثل الذي يقوم بتشغيل حلقة لا نهائية. في أي حال ، سيكون رمز غير المنضبط الذي سيستهلك موارد خادمنا إلى أجل غير مسمى. من أجل التحكم بطريقة ما في هذه العملية ، نحتاج إلى القدرة على مقاطعة تنفيذ البرامج النصية. عند استخدام محركات مكتوبة بصافي .NET (على سبيل المثال ، Jint أو Jurassic أو NiL.JS) ، يمكننا دائمًا البدء في تنفيذ تعليمات JS البرمجية كمهمة مع إمكانية الإلغاء ، ولكن هذا النهج لن يعمل مع المحركات الأخرى. لحسن الحظ بالنسبة لنا ، تحتوي محركات C ++ على آليات مضمّنة لمقاطعة البرامج النصية.
لتوفير الوصول إلى هذه الآليات ، تمت إضافة خاصية SupportsScriptInterruption
وأسلوب Interrupt
إلى واجهة IJsEngine
. نظرًا لأن جميع المحركات لا تدعم هذه الميزة ، يجب عليك دائمًا التحقق من قيمة الخاصية SupportsScriptInterruption
قبل استدعاء طريقة Interrupt
(إذا كان في الإصدارات السابقة من JavaScript Engine Switcher كان عليك تشغيل أداة تجميع مجمعي البيانات المهملة ، فسوف تفهم على الفور ما أتحدث عنه):
if (engine.SupportsScriptInterruption) { engine.Interrupt(); }
وتحتاج إلى استدعاء هذه الطريقة في مؤشر ترابط منفصل يختلف عن مؤشر الترابط الذي يتم فيه تنفيذ البرامج النصية. بعد استدعاء أسلوب Interrupt
، CallFunction
Execute*
و CallFunction
بـ JsInterruptedException
.
نظرًا لأن واجهة برمجة التطبيقات هذه منخفضة المستوى ، فمن المستحسن استخدام طرق الامتداد مثل ما يلي للمهام الموضحة في بداية القسم:
using System; #if !NET40 using System.Runtime.ExceptionServices; #endif using System.Threading; using System.Threading.Tasks; using JavaScriptEngineSwitcher.Core; #if NET40 using JavaScriptEngineSwitcher.Core.Extensions; #endif using JavaScriptEngineSwitcher.Core.Resources; … /// <summary> /// Extension methods for <see cref="IJsEngine"/> /// </summary> public static class JsEngineExtensions { /// <summary> /// Evaluates an expression within a specified time interval /// </summary> /// <typeparam name="T">Type of result</typeparam> /// <param name="engine">JS engine</param> /// <param name="expression">JS expression</param> /// <param name="timeoutInterval">Interval to wait before the /// script execution times out</param> /// <param name="documentName">Document name</param> /// <returns>Result of the expression</returns> /// <exception cref="ObjectDisposedException"/> /// <exception cref="ArgumentNullException"/> /// <exception cref="ArgumentException"/> /// <exception cref="JsCompilationException"/> /// <exception cref="JsTimeoutException"/> /// <exception cref="JsRuntimeException"/> /// <exception cref="JsException"/> public static T Evaluate<T>(this IJsEngine engine, string expression, TimeSpan timeoutInterval, string documentName) { if (engine == null) { throw new ArgumentNullException(nameof(engine)); } if (engine.SupportsScriptInterruption) { using (var timer = new Timer(state => engine.Interrupt(), null, timeoutInterval, #if NET40 new TimeSpan(0, 0, 0, 0, -1))) #else Timeout.InfiniteTimeSpan)) #endif { try { return engine.Evaluate<T>(expression, documentName); } catch (JsInterruptedException e) { throw new JsTimeoutException( Strings.Runtime_ScriptTimeoutExceeded, e.EngineName, e.EngineVersion, e ); } } } else { #if NET40 Task<T> task = Task.Factory.StartNew(() => #else Task<T> task = Task.Run(() => #endif { return engine.Evaluate<T>(expression, documentName); }); bool isCompletedSuccessfully = false; try { isCompletedSuccessfully = task.Wait(timeoutInterval); } catch (AggregateException e) { Exception innerException = e.InnerException; if (innerException != null) { #if NET40 innerException.PreserveStackTrace(); throw innerException; #else ExceptionDispatchInfo.Capture(innerException).Throw(); #endif } else { throw; } } if (isCompletedSuccessfully) { return task.Result; } else { throw new JsTimeoutException( Strings.Runtime_ScriptTimeoutExceeded, engine.Name, engine.Version ); } } } … } …
هذه الطريقة عبارة عن وظيفة إضافية على طريقة المحرك Evaluate<T>
، والذي يسمح باستخدام المعلمة timeoutInterval
لتعيين مهلة انتظار تنفيذ البرنامج النصي. مبدأ تشغيل طريقة التمديد هذه بسيط للغاية. أولاً ، نتحقق مما إذا كان محركنا يدعم آلية المقاطعة المدمجة أم لا. إذا كان الأمر timeoutInterval
، فنحن ننشئ Timer
لفئة Timer
، والتي بعد فترة الفاصل الزمني المحددة في معلمة timeoutInterval
، تبدأ طريقة Interrupt
، ثم نسميها طريقة JsInterruptedException
Evaluate<T>
، وفي حالة حدوث خطأ ، يمكنك التقاط JsInterruptedException
في JsTimeoutException
. إذا كان المحرك لا يدعم المقاطعات ، فقم بإنشاء مثيل لفئة Task
التي تدير طريقة Evaluate<T>
الخاصة بالمحرك ، ثم قم بتعيين الفاصل الزمني للانتظار للمهمة ليتم تنفيذها على قدم المساواة مع القيمة من المعلمة timeoutInterval
، وإذا اكتملت المهمة حسب المهلة ، JsTimeoutException
نوع JsTimeoutException
. فيما يلي مثال لاستخدام طريقة التمديد هذه:
using System; … using JavaScriptEngineSwitcher.Core; using JavaScriptEngineSwitcher.Core.Helpers; … class Program { … static void Main(string[] args) { const string expression = @"function getRandomInt(minValue, maxValue) { minValue = Math.ceil(minValue); maxValue = Math.floor(maxValue); return Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue; } function sleep(millisecondsTimeout) { var totalMilliseconds = new Date().getTime() + millisecondsTimeout; while (new Date().getTime() < totalMilliseconds) { } } var randomNumber = getRandomInt(1, 10); sleep(randomNumber * 1000); randomNumber;"; using (IJsEngine engine = JsEngineSwitcher.Current.CreateDefaultEngine()) { try { int result = engine.Evaluate<int>(expression, TimeSpan.FromSeconds(3), "randomNumber.js"); Console.WriteLine(" = {0}", result); } catch (JsTimeoutException) { Console.WriteLine(" JavaScript " + " !"); } catch (JsException e) { Console.WriteLine(" JavaScript- " + "!"); Console.WriteLine(); Console.WriteLine(JsErrorHelpers.GenerateErrorDetails(e)); } } } … } …
باستخدام طريقة التمديد ، نحسب نتيجة تعبير JS. نتيجة التعبير عبارة عن عدد صحيح عشوائي ، وهذا الرقم هو أيضًا عدد الثواني التي يتم خلالها تأخير هذا التعبير. أيضًا ، عند استدعاء طريقة التمديد ، نحدد فترة انتظار مدتها 3 ثوان. نظرًا لأن مدة حساب التعبير تختلف من 1 إلى 10 ثوانٍ ، في حالة واحدة ، سيتم عرض نتيجة التعبير على وحدة التحكم ، وفي رسالة أخرى ، حول المهلة.
لا ينبغي أن يكون من الصعب عليك إعادة تشكيل طريقة الامتداد هذه لاستدعاء أساليب المحرك الأخرى (على سبيل المثال ، CallFunction
Execute*
، CallFunction
أو الإصدارات الأخرى المحملة بشكل زائد من طريقة التقييم). لم أقرر حتى الآن ما إذا كنت سأضيف طرق التمديد هذه إلى المكتبة نفسها ، لأنه لم يتضح بعد حجم الطلب.
البرنامج النصي precompilation
طلب مني إضافة دعم للتجميع الأولي للنصوص في عام 2015 . في تلك اللحظة ، لم يكن من الواضح كيف يمكن لهذه الوظيفة أن تنسجم مع مفهوم الواجهة الموحدة. ولكن مع مرور الوقت ، بدأ دعم ميزات إمكانية الوصول مثل تجميع البيانات المهملة وانقطاع البرنامج النصي في الظهور تدريجياً في JavaScript Engine Switcher. في المراحل الأخيرة من العمل على الإصدار الثالث ، جاء دور التجميع الأولي.
باستخدام التحويل المسبق ، يمكنك تجميع برنامج نصي مرة واحدة ثم استخدامه عدة مرات لتهيئة محركات JS. نظرًا لحقيقة أن البرنامج النصي المترجم مسبقًا لا يحتاج إلى تحليل ، فإن تهيئة المحركات ستكون أسرع.
في الوقت الحالي ، يتم دعم التجميع المسبق بواسطة 5 وحدات محول: ChakraCore و Jint و Jurassic و MSIE (فقط في أوضاع JsRT) و V8. لتوفير الوصول إلى آليات المحرك المناظرة ، تمت إضافة خاصية SupportsScriptPrecompilation
و 3 طرق جديدة إلى واجهة IJsEngine
: Precompile
و PrecompileFile
و PrecompileResource
. ترجع أساليب Precompile*
مثيل كائن يقوم بتنفيذ واجهة IPrecompiledScript
. هذا الكائن هو برنامج نصي تم ترجمته مسبقًا والذي يمكن استخدامه بواسطة مثيلات مختلفة من المحركات (نسخة محمّلة بشكل زائد من طريقة Execute
تخدم لهذا الغرض). فكر في مثال بسيط لاستخدام واجهة برمجة التطبيقات هذه:
using System; … using JavaScriptEngineSwitcher.Core; using JavaScriptEngineSwitcher.Core.Helpers; … class Program { … static void Main(string[] args) { const string sourceCode = @"function declinationOfSeconds(number) { var result, titles = ['', '', ''], titleIndex, cases = [2, 0, 1, 1, 1, 2], caseIndex ; if (number % 100 > 4 && number % 100 < 20) { titleIndex = 2; } else { caseIndex = number % 10 < 5 ? number % 10 : 5; titleIndex = cases[caseIndex]; } result = number + ' ' + titles[titleIndex]; return result; }"; const string functionName = "declinationOfSeconds"; const int itemCount = 4; int[] inputSeconds = new int[itemCount] { 0, 1, 42, 600 }; string[] outputStrings = new string[itemCount]; IJsEngineSwitcher engineSwitcher = JsEngineSwitcher.Current; IPrecompiledScript precompiledCode = null; using (var engine = engineSwitcher.CreateDefaultEngine()) { if (!engine.SupportsScriptPrecompilation) { Console.WriteLine("{0} {1} " + " !", engine.Name, engine.Version); return; } try { precompiledCode = engine.Precompile(sourceCode, "declinationOfSeconds.js"); engine.Execute(precompiledCode); outputStrings[0] = engine.CallFunction<string>(functionName, inputSeconds[0]); } catch (JsCompilationException e) { Console.WriteLine(" " + " !"); Console.WriteLine(); Console.WriteLine(JsErrorHelpers.GenerateErrorDetails(e)); return; } catch (JsException e) { Console.WriteLine(" JavaScript- " + "!"); Console.WriteLine(); Console.WriteLine(JsErrorHelpers.GenerateErrorDetails(e)); return; } } for (int itemIndex = 1; itemIndex < itemCount; itemIndex++) { using (var engine = engineSwitcher.CreateDefaultEngine()) { try { engine.Execute(precompiledCode); outputStrings[itemIndex] = engine.CallFunction<string>( functionName, inputSeconds[itemIndex]); } catch (JsException e) { Console.WriteLine(" JavaScript- " + " !"); Console.WriteLine(); Console.WriteLine(JsErrorHelpers.GenerateErrorDetails(e)); return; } } } for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) { Console.WriteLine(outputStrings[itemIndex]); } } … } …
, . SupportsScriptPrecompilation
, , , . Precompile
, , JsCompilationException
. C Execute
, .. . CallFunction
declinationOfSeconds
. . , , . 3 . , , . , , .
, , . , , . ReactJS.NET. JSPool , ( System.Runtime.Caching.MemoryCache
.NET Framework 4.X, System.Web.Caching.Cache
ASP.NET 4.X Microsoft.Extensions.Caching.Memory.IMemoryCache
ASP.NET Core). , ( ). , ReactJS.NET.
ReactJS.NET . . AllowJavaScriptPrecompilation
true
.
ASP.NET 4.X App_Start/ReactConfig.cs
:
… public static class ReactConfig { public static void Configure() { ReactSiteConfiguration.Configuration … .SetAllowJavaScriptPrecompilation(true) … ; … } } …
ASP.NET Core Startup.cs
:
… public class Startup { … public void Configure(IApplicationBuilder app, IHostingEnvironment env) { … app.UseReact(config => { config … .SetAllowJavaScriptPrecompilation(true) … ; }); app.UseStaticFiles(); … } } …
, . JsExecutionBenchmark , BenchmarkDotNet . JS- . 14,9 , , . JavaScript Engine Switcher ( 3.0.4).
, .NET Framework 4.7.2:
| . | . | Gen 0 . . | Gen 1 . . | Gen 2 . . | . |
---|
ChakraCore | | 41,72 | - | - | - | 74,46 |
| 35,07 | - | - | - | 91,79 |
Jint | | 27,19 | 2 812,50 | 1 343,75 | - | 16 374,58 |
| 15,54 | 1 296,88 | 640,63 | 31,25 | 7 521,49 |
Jurassic | | 455,70 | 2 000,00 | 1 000,00 | - | 15 575,28 |
| 78,70 | 1 000,00 | - | - | 7 892,94 |
MSIE Chakra IE JsRT | | 30,97 | - | - | - | 77,75 |
| 24,40 | - | - | - | 90,58 |
MSIE Chakra Edge JsRT | | 33,14 | - | - | - | 78,40 |
| 32,86 | - | - | - | 95,48 |
V8 | | 41,10 | - | - | - | 79,33 |
| 39,25 | - | - | - | 96,17 |
, .NET — Jurassic 5,79 , Jint 1,75 a. 2 , 2 . . Jurassic, : Jurassic JS- IL- Reflection.Emit, . , , . Jint .NET-, . Jint , . , Jint , , . Jint Jurassic .
, C++, MSIE Chakra IE JsRT — 26,93%. ChakraCore (18,96%), V8 (4,71%) MSIE Chakra Edge JsRT (0,85%). Internet Explorer Edge. . ( ) . , - 12-17 ( ). . . .
.NET Core 2.0 ( V8 , Microsoft ClearScript, , .NET Core):
| . | . | Gen 0 . . | Gen 1 . . | Gen 2 . . | . |
---|
ChakraCore | | 43,65 | - | - | - | 18,07 |
| 36,37 | - | - | - | 16,59 |
Jint | | 24,87 | 2 750,00 | 1 375,00 | - | 16 300,25 |
| 15,25 | 1 281,25 | 593,75 | 62,50 | 7 447,44 |
Jurassic | | 469,97 | 2 000,00 | 1 000,00 | - | 15 511,70 |
| 80,72 | 1 000,00 | - | - | 7 845,98 |
MSIE Chakra IE JsRT | | 31,50 | - | - | - | 20,28 |
| 24,52 | - | - | - | 18,78 |
MSIE Chakra Edge JsRT | | 35,54 | - | - | - | 20,45 |
| 31,44 | - | - | - | 18,99 |
. , — MSIE Chakra Edge JsRT (7,69%). Chakra .
ChakraCore MSIE
, Microsoft, , . IIS (256 32- 512 64-), ASP.NET JS- (, TypeScript) . JavaScript Engine Switcher . , Node.js (492 32- 984 64-). , , - . ChakraCore MSIE MaxStackSize
, . Node.js. , .
NiL.JS
- NiL , NiL.JS . NiL.JS — JS-, .NET. 2014 , Jurassic Jint. — . , .
.NET Framework 4.7.2 :
| . | Gen 0 . . | Gen 1 . . | Gen 2 . . | . |
---|
Jint | 27,19 | 2 812,50 | 1 343,75 | - | 16 374,58 |
Jurassic | 455,70 | 2 000,00 | 1 000,00 | - | 15 575,28 |
NiL | 17,80 | 1 000,00 | - | - | 4 424,09 |
.NET Core 2.0 :
| . | Gen 0 . . | Gen 1 . . | Gen 2 . . | . |
---|
Jint | 24,87 | 2 750,00 | 1 375,00 | - | 16 300,25 |
Jurassic | 469,97 | 2 000,00 | 1 000,00 | - | 15 511,70 |
NiL | 19,67 | 1 000,00 | - | - | 4 419,95 |
. , ( ). , Jint 2016 , . 3.0.0 Beta 1353 , Jint 2,4 , NiL.JS 2.5.1200, .
NiL.JS . - , - , . Bundle Transformer, JavaScript Engine Switcher, Hogan Handlebars, ReactJS.NET. NiL.JS.
المراجع
- JavaScript Engine Switcher GitHub
- JavaScript Engine Switcher
- « JavaScript Engine Switcher 2.X»
- JSPool GitHub
- ReactJS.NET
- ReactJS.NET GitHub