
JavaScript Engine Switcher awalnya dibuat sebagai perpustakaan bantu dan pengembangannya sebagian besar ditentukan oleh kebutuhan perpustakaan yang menggunakannya. Bahkan, masing-masing versi utamanya menyelesaikan satu atau beberapa tugas utama yang diperlukan untuk pengembangan lebih lanjut dari perpustakaan lain:
- Pada versi pertama, tugas ini adalah menambahkan sebanyak mungkin modul adaptor untuk mesin JS populer yang mendukung platform .NET. Dan ini memberi pengguna Bundle Transformer fleksibilitas: pada komputer pengembang, mereka dapat menggunakan modul MSIE yang mendukung debugging kode JS menggunakan Visual Studio, dan pada server yang tidak memiliki versi Internet Explorer modern atau tidak diinstal sama sekali, mereka dapat menggunakan Modul V8 . Beberapa bahkan berhasil menjalankan Bundle Transformer di Mono di Linux dan Mac menggunakan modul Jurassic dan Jint .
- Tugas utama dari versi kedua adalah implementasi dukungan .NET Core, yang diperlukan untuk versi baru dari perpustakaan ReactJS.NET . Tugas penting lainnya adalah pembuatan modul lintas-platform yang dapat dengan cepat memproses sejumlah besar kode JS (modul Jurassic dan Jint tidak cocok untuk ini), dan setelah sejumlah perbaikan, modul ChakraCore menjadi modul semacam itu.
- Dalam versi ketiga, fokus utamanya adalah meningkatkan integrasi dengan perpustakaan ReactJS.NET dan meningkatkan produktivitas.
Dalam artikel ini, kami akan mempertimbangkan beberapa inovasi dari versi ketiga, yang bagi banyak orang ternyata tidak terlihat bahkan setelah membaca teks rilis dan bagian dokumentasi "Cara meningkatkan aplikasi ke versi 3.X" : perubahan dalam kelas JsEngineSwitcher
, JsEngineSwitcher
kembali pengecualian, pesan kesalahan yang lebih informatif, gangguan dan kompilasi skrip pendahuluan, kemampuan untuk mengubah ukuran tumpukan maksimum dalam modul ChakraCore dan MSIE, serta modul baru berdasarkan NiL.JS.
Perubahan ke Kelas JsEngineSwitcher
Dalam versi baru, kelas JsEngineSwitcher
mengimplementasikan antarmuka IJsEngineSwitcher
dan bukan lagi singleton (Anda bisa instantiate menggunakan operator new
). Untuk mendapatkan instance global, alih-alih properti Instance
, gunakan properti Current
. Properti Current
ini, tidak seperti properti Instance
sudah tidak digunakan lagi, memiliki tipe pengembalian IJsEngineSwitcher
. Juga, properti Current
memiliki penyetel, yang dengannya Anda dapat mengganti implementasi standar dengan milik Anda sendiri:
JsEngineSwitcher.Current = new MyJsEngineSwitcher();
Dalam aplikasi web ASP.NET Core yang memiliki paket JavaScriptEngineSwitcher.Extensions.MsDependencyInjection diinstal, implementasinya diganti menggunakan AddJsEngineSwitcher
ekstensi AddJsEngineSwitcher
:
β¦ using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; β¦ public class Startup { β¦ public void ConfigureServices(IServiceCollection services) { β¦ services.AddJsEngineSwitcher(new MyJsEngineSwitcher(), options => β¦ ) β¦ ; β¦ } β¦ } β¦
Perubahan ini hampir selalu βmemecahβ aplikasi atau pustaka yang menggunakan versi JavaScript Engine Switcher sebelumnya. Karena itu, Anda perlu membuat perubahan berikut pada kode Anda:
- Ubah jenis variabel, parameter, atau properti dari
JsEngineSwitcher
ke IJsEngineSwitcher
. - Alih-alih properti
Instance
, gunakan properti Current
mana-mana.
Perlu juga dicatat bahwa bagi sebagian besar pengembang, perubahan ini tidak akan bermanfaat khususnya, karena tujuan utama mereka adalah untuk menyederhanakan tes unit (misalnya, sebelumnya, dalam tes unit perpustakaan ReactJS.NET, kunci telah digunakan , tetapi sekarang Anda dapat melakukannya tanpa mereka ).
Reorganisasi Pengecualian
Sebelum versi ketiga, sebagian besar kesalahan mesin JS berubah menjadi JsRuntimeException
, dan hanya kesalahan yang terjadi selama proses inisialisasi mesin berubah menjadi JsEngineLoadException
. Ada juga kelas dasar JsException
, dari mana dua jenis pengecualian di atas diwariskan, yang memungkinkan untuk mencegat sepenuhnya semua kesalahan yang terjadi selama pengoperasian mesin JS. Meskipun ada kelemahan yang jelas, organisasi pengecualian ini sangat cocok dengan konsep antarmuka terpadu untuk mengakses kemampuan dasar mesin JS.
Tetapi dengan implementasi interupsi dan pra-kompilasi skrip (saya akan membahasnya di bagian berikut), perlu muncul pendekatan baru untuk mengatur pengecualian. Hal pertama yang harus dilakukan adalah menambahkan tipe pengecualian baru - JsInterruptedException
, yang diperlukan untuk memberi tahu pengguna tentang gangguan eksekusi skrip. Maka itu perlu untuk secara eksplisit membagi semua kesalahan yang terjadi selama pemrosesan skrip menjadi dua kelompok: kesalahan kompilasi (parsing) dan kesalahan runtime. Itu juga diperlukan untuk memisahkan semua jenis kesalahan khusus Chakra dan V8, yang tidak terkait dengan pemrosesan skrip. Itu juga perlu untuk mempertimbangkan keberadaan pengecualian di mesin Jint yang terjadi ketika periode waktu habis untuk menjalankan skrip (batas waktu) telah berlalu. Akibatnya, pendekatan baru untuk organisasi pengecualian dibentuk, yang dapat direpresentasikan sebagai struktur hierarkis berikut:
JsException
JsEngineException
JsFatalException
JsScriptException
JsCompilationException
JsRuntimeException
JsInterruptedException
JsTimeoutException
JsUsageException
JsEngineNotFoundException
*
* - pengecualian ini tidak terjadi pada level mesin JS, tetapi pada level JavaScript Engine Switcher.
Saya pikir hierarki pengecualian yang disajikan di atas tidak perlu komentar, karena nama-nama pengecualian tersebut berbicara sendiri. Dengan pendekatan ini, kami tidak hanya mendapatkan lebih banyak informasi tentang penyebab kesalahan, tetapi juga dapat lebih fleksibel menangani beberapa jenis pengecualian tertentu.
Format pesan kesalahan terpadu
Masalah lain dengan versi sebelumnya dari JavaScript Engine Switcher adalah kesulitan dalam menemukan kesalahan yang terjadi saat memproses skrip. Dari properti Pengecualian Message
, sulit untuk memahami persis di mana kesalahan terjadi dalam kode, jadi saya harus menganalisis properti pengecualian lainnya, yang tidak selalu nyaman. Selain itu, himpunan properti yang ada juga tidak cukup.
Oleh karena itu, 2 properti baru ditambahkan ke kelas JsScriptException
:
- Jenis - jenis kesalahan JavaScript (misalnya,
SyntaxError
atau TypeError
); - DocumentName - nama dokumen (biasanya diekstraksi dari nilai parameter berikut:
documentName
dari metode Execute
and Evaluate
, path
metode ExecuteFile
, resourceName
metode ExecuteResource
, dll.);
Satu properti baru juga telah ditambahkan ke kelas JsRuntimeException - CallStack , yang berisi representasi string dari tumpukan panggilan.
Sebelumnya, nilai dari properti serupa dari pengecualian .NET asli atau representasi string dari kesalahan JavaScript hanya disalin ke properti Message
. Seringkali, pesan kesalahan di mesin JS yang berbeda tidak hanya berbeda dalam format, tetapi juga dalam jumlah informasi berguna yang disajikan di dalamnya. Misalnya, karena kurangnya informasi nomor baris dan kolom dalam beberapa pesan kesalahan, pengembang perpustakaan ReactJS.NET terpaksa melempar kembali pengecualian yang diterima dari JavaScript Engine Switcher.
Oleh karena itu, saya memutuskan untuk membuat pesan kesalahan sendiri di tingkat modul adaptor, yang akan memiliki format tunggal (terpadu). Format ini menggunakan semua informasi kesalahan yang tersedia: jenis, deskripsi, nama dokumen, nomor baris, nomor kolom, fragmen kode dan tumpukan panggilan. Sebagai dasar untuk format baru, saya mengambil format kesalahan dari perpustakaan Microsoft ClearScript .
Di bawah ini adalah pesan tentang kesalahan kompilasi yang sama yang dihasilkan oleh modul adaptor yang berbeda:
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
Contoh serupa untuk kesalahan runtime:
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
Seperti yang dapat Anda lihat dari contoh, beberapa mesin JS memberi kami deskripsi kesalahan dan nomor kolom yang sama sekali berbeda, dan kami tidak dapat selalu mendapatkan serangkaian data kesalahan yang lengkap, tetapi meskipun ada kekurangan ini, format terpadu ini memberi kami informasi lebih banyak tentang lokasi kesalahan daripada pesan kesalahan asli.
Tips Penerapan Majelis Asli
Penyebab utama kesalahan ketika bekerja dengan versi kedua dari JavaScript Engine Switcher adalah bahwa banyak pengembang lupa untuk menginstal paket NuGet yang berisi rakitan asli untuk modul ChakraCore dan V8. Pada suatu waktu, sebuah pos di bugtracker ReactJS.NET juga dikhususkan untuk masalah ini ( terjemahan Rusia juga tersedia). Sekarang, kesalahan seperti itu hanya ditemui oleh pemula yang, karena alasan tertentu, tidak membaca dokumentasi.
Para penulis ReactJS.NET mencoba meminimalkan jumlah kesalahan seperti itu dengan menggunakan petunjuk di dalam pesan kesalahan, tetapi implementasi pendekatan ini yang tidak terlalu berhasil menyebabkan kebingungan yang lebih besar . Gagasan tentang petunjuk itu bagus, tetapi membutuhkan implementasi yang berbeda secara mendasar, yaitu implementasi pada tingkat modul adaptor mesin JS. Di versi baru JavaScript Engine Switcher, petunjuk seperti itu ditambahkan ke pesan kesalahan saat membungkus pengecualian TypeLoadException
dan TypeLoadException
JsEngineLoadException
(lihat contoh implementasi untuk modul ChakraCore , V8 dan Vroom ). Apalagi tips ini cerdas, karena generasi mereka memperhitungkan sejumlah faktor: jenis sistem operasi, arsitektur prosesor, dan runtime (.NET Framework, .NET Core atau Mono).
Misalnya, ketika menggunakan modul ChakraCore tanpa perakitan asli dalam proses 64-bit pada sistem operasi Windows, pesan kesalahan akan terlihat seperti ini:
Gagal membuat instance dari ChakraCoreJsEngine. Kemungkinan besar itu terjadi, karena perakitan 'ChakraCore.dll' atau salah satu dari dependensinya tidak ditemukan. Cobalah untuk menginstal paket JavaScriptEngineSwitcher.ChakraCore.Native.win-x64 via NuGet. Selain itu, Anda masih perlu menginstal Microsoft Visual C ++ Redistributable untuk Visual Studio 2017 ( https://www.visualstudio.com/downloads/#microsoft-visual-c-redistributable-for-visual-studio-2017 ).
Pesan kesalahan memberikan petunjuk bahwa Anda perlu menginstal paket JavaScript NuGetEngineSwitcher.ChakraCore.Native.win-x64, dan juga menyebutkan bahwa ChakraCore untuk Windows memerlukan komponen Microsoft Visual C ++ yang dapat didistribusikan untuk Visual Studio 2017 agar berfungsi. terjadi dalam proses 32-bit, pengguna akan diminta untuk menginstal paket JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.
Pesan kesalahan serupa di Linux di .NET Core akan terlihat seperti ini:
Gagal membuat instance dari ChakraCoreJsEngine. Kemungkinan besar itu terjadi, karena perakitan 'libChakraCore.so' atau salah satu dependensinya tidak ditemukan. Cobalah untuk menginstal paket JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 via NuGet.
Dalam hal ini, disarankan untuk menginstal paket JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64.
Ketika diluncurkan di Mono, prompt lain akan ditampilkan:
... JavaScriptEngineSwitcher.ChakraCore.Native.linux- * paket tidak mendukung instalasi di bawah Mono, tetapi Anda dapat menginstal rakitan asli secara manual ( https://github.com/Taritsyn/JavaScriptEngineSwitcher/wiki/ChakraCore#linux ).
Karena paket JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 hanya kompatibel dengan .NET Core, tooltip akan memberikan tautan ke instruksi untuk secara manual menggunakan perakitan asli di Linux.
Banyak lagi contoh yang bisa diberikan, tetapi ini tidak masuk akal.
Membatalkan Eksekusi Skrip
Ketika kami memberi pengguna kesempatan untuk mengeksekusi kode JS sewenang-wenang di server, kami dihadapkan pada satu masalah yang sangat serius - kami tidak tahu berapa lama waktu yang diperlukan untuk mengeksekusi kode ini. Ini mungkin sejumlah besar kode tidak optimal atau kode yang menjalankan infinite loop. Bagaimanapun, itu akan menjadi kode yang tidak terkontrol yang akan menghabiskan sumber daya dari server kami tanpa batas. Untuk setidaknya mengendalikan proses ini, kita perlu kemampuan untuk menghentikan eksekusi skrip. Saat menggunakan mesin yang ditulis dalam .NET murni (misalnya, Jint, Jurassic atau NiL.JS), kami selalu dapat mulai menjalankan kode JS sebagai tugas dengan kemampuan untuk membatalkan, tetapi pendekatan ini tidak akan bekerja untuk mesin lain. Untungnya bagi kami, mesin C ++ memiliki mekanisme bawaan untuk mengganggu skrip.
Untuk memberikan akses ke mekanisme ini, properti SupportsScriptInterruption
dan metode Interrupt
ditambahkan ke antarmuka IJsEngine
. Karena tidak semua mesin mendukung fitur ini, Anda harus selalu memeriksa nilai properti SupportsScriptInterruption
sebelum memanggil metode Interrupt
(jika dalam versi sebelumnya dari JavaScript Engine Switcher Anda harus menjalankan pengumpul sampah secara manual, Anda akan segera mengerti apa yang saya bicarakan):
if (engine.SupportsScriptInterruption) { engine.Interrupt(); }
Dan Anda perlu memanggil metode ini di utas terpisah berbeda dari utas tempat skrip dieksekusi. Setelah memanggil metode Interrupt
, semua yang sebelumnya menjalankan metode Evaluate
, Execute*
dan CallFunction
akan diakhiri dengan JsInterruptedException
.
Karena API ini tingkat rendah, disarankan untuk menggunakan metode ekstensi seperti berikut ini untuk tugas-tugas yang dijelaskan di awal bagian:
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 ); } } } β¦ } β¦
Metode ini merupakan add-on pada metode engine Evaluate<T>
, yang memungkinkan penggunaan parameter timeoutInterval
mengatur batas waktu menunggu skrip dijalankan. Prinsip operasi metode ekstensi ini sangat sederhana. Pertama, kami memeriksa apakah mesin kami mendukung mekanisme interupsi bawaan. Jika ya, kita membuat instance kelas Timer
, yang, setelah interval timeoutInterval
ditentukan dalam parameter timeoutInterval
, memulai metode Interrupt
, kemudian kita memanggil metode mesin Evaluate<T>
, dan jika ada kesalahan, tangkap JsInterruptedException
dan bungkus dalam JsTimeoutException
. Jika mesin tidak mendukung interupsi, kemudian buat instance kelas Task
yang memulai metode Evaluate<T>
, kemudian tetapkan interval tunggu untuk tugas yang harus diselesaikan sama dengan nilai dari parameter timeoutInterval
, dan jika tugas selesai dengan batas waktu, kemudian JsTimeoutException
jenis JsTimeoutException
. Berikut ini adalah contoh penggunaan metode ekstensi ini:
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)); } } } β¦ } β¦
Menggunakan metode ekstensi, kami menghitung hasil dari ekspresi JS. Hasil dari ekspresi adalah bilangan bulat acak, dan angka ini juga merupakan jumlah detik dimana ekspresi ini tertunda. Juga, saat memanggil metode ekstensi, kami menentukan interval tunggu 3 detik. Karena waktu untuk menghitung ekspresi bervariasi dari 1 hingga 10 detik, dalam satu kasus hasil ekspresi akan ditampilkan di konsol, dan di lain, pesan tentang batas waktu.
Seharusnya tidak sulit bagi Anda untuk membuat ulang metode ekstensi ini untuk memanggil metode engine lainnya (misalnya, untuk Execute*
, CallFunction
, atau versi lain dari metode Evaluate
). Sejauh ini saya belum memutuskan apakah saya akan menambahkan metode ekstensi seperti itu ke perpustakaan itu sendiri, karena belum jelas berapa banyak mereka akan diminati.
Prompilasi skrip
Saya diminta untuk menambahkan dukungan untuk penyusunan skrip awal pada tahun 2015 . Pada saat itu, tidak jelas bagaimana fungsi tersebut dapat masuk ke dalam konsep antarmuka terpadu. Tetapi seiring berjalannya waktu, dukungan untuk fitur aksesibilitas seperti pengumpulan sampah dan gangguan skrip secara bertahap mulai muncul di JavaScript Engine Switcher. Pada tahap terakhir pekerjaan pada versi ketiga, giliran datang ke kompilasi awal.
Menggunakan pra-kompilasi, Anda dapat mengkompilasi skrip sekali dan kemudian menggunakannya berkali-kali untuk menginisialisasi mesin JS. Karena fakta bahwa skrip pra-kompilasi tidak memerlukan parsing, inisialisasi mesin akan lebih cepat.
Saat ini, pra-kompilasi didukung oleh 5 modul adaptor: ChakraCore, Jint, Jurassic, MSIE (hanya dalam mode JsRT) dan V8. Untuk memberikan akses ke mekanisme mesin yang sesuai, properti SupportsScriptPrecompilation
dan 3 metode baru ditambahkan ke antarmuka Precompile
: Precompile
, PrecompileFile
, dan PrecompileResource
. Precompile*
mengembalikan instance objek yang mengimplementasikan antarmuka IPrecompiledScript
. Objek ini adalah skrip pra-kompilasi yang dapat digunakan oleh mesin contoh yang berbeda (versi kelebihan dari metode Execute
berfungsi untuk tujuan ini). Pertimbangkan contoh sederhana menggunakan API ini:
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.
Referensi
- JavaScript Engine Switcher GitHub
- JavaScript Engine Switcher
- Β« JavaScript Engine Switcher 2.XΒ»
- JSPool GitHub
- ReactJS.NET
- ReactJS.NET GitHub