
O JavaScript Engine Switcher foi originalmente criado como uma biblioteca auxiliar e seu desenvolvimento foi amplamente determinado pelas necessidades das bibliotecas que o usavam. De fato, cada uma de suas principais versões resolveu uma ou várias tarefas principais necessárias para o desenvolvimento adicional de outras bibliotecas:
- Na primeira versão, essa tarefa foi incluir o máximo de módulos adaptadores para os populares mecanismos JS que suportam a plataforma .NET possível. E isso deu aos usuários do Bundle Transformer certa flexibilidade: nos computadores do desenvolvedor, eles poderiam usar o módulo MSIE que suporta a depuração de código JS usando o Visual Studio e em servidores que não tinham uma versão moderna do Internet Explorer ou que não estava instalada, eles poderiam usar Módulo V8 . Alguns até conseguiram rodar o Bundle Transformer em Mono no Linux e Mac usando os módulos Jurassic e Jint .
- A principal tarefa da segunda versão foi a implementação do suporte ao .NET Core, necessário para a nova versão da biblioteca ReactJS.NET . Outra tarefa importante foi a criação de um módulo de plataforma cruzada que pode processar rapidamente grandes quantidades de código JS (os módulos Jurassic e Jint não eram adequados para isso) e, após várias melhorias, o módulo ChakraCore se tornou um módulo.
- Na terceira versão, o foco principal era melhorar a integração com a biblioteca ReactJS.NET e melhorar a produtividade.
Neste artigo, consideraremos algumas inovações da terceira versão, que para muitos se revelaram óbvias, mesmo após a leitura do texto do release e da seção de documentação “Como atualizar aplicativos para a versão 3.X” : mudanças na classe JsEngineSwitcher
, reorganização de exceções, mensagens de erro mais informativas, interrupção e compilação preliminar de scripts, a capacidade de alterar o tamanho máximo da pilha nos módulos ChakraCore e MSIE, bem como um novo módulo baseado no NiL.JS.
Alterações na classe JsEngineSwitcher
Na nova versão, a classe JsEngineSwitcher
implementa a interface IJsEngineSwitcher
e não é mais um singleton (você pode instancia-la usando o new
operador). Para obter uma instância global, use a propriedade Current
em vez da propriedade Instance
. A propriedade Current
, diferentemente da propriedade Instance
descontinuada, tem o tipo de retorno IJsEngineSwitcher
. Além disso, a propriedade Current
possui um setter, com o qual você pode substituir a implementação padrão pela sua:
JsEngineSwitcher.Current = new MyJsEngineSwitcher();
Nos aplicativos Web ASP.NET Core que possuem o pacote JavaScriptEngineSwitcher.Extensions.MsDependencyInjection instalado, a implementação é substituída usando o AddJsEngineSwitcher
extensão AddJsEngineSwitcher
:
… using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; … public class Startup { … public void ConfigureServices(IServiceCollection services) { … services.AddJsEngineSwitcher(new MyJsEngineSwitcher(), options => … ) … ; … } … } …
Essas alterações quase sempre "quebram" aplicativos ou bibliotecas que usam a versão anterior do JavaScript Engine Switcher. Portanto, você precisa fazer as seguintes alterações no seu código:
- Altere o tipo de variáveis, parâmetros ou propriedades de
JsEngineSwitcher
para IJsEngineSwitcher
. - Em vez da propriedade
Instance
, use a propriedade Current
em qualquer lugar.
Também é importante notar que, para a maioria dos desenvolvedores, essas alterações não serão de grande benefício, porque seu principal objetivo era simplificar os testes de unidade (por exemplo, anteriormente, nos testes de unidade da biblioteca ReactJS.NET, os bloqueios eram usados , mas agora você pode fazer sem eles ).
Reorganização de exceções
Antes da terceira versão, a maioria dos erros do mecanismo JS se transformava em JsRuntimeException
tipo JsRuntimeException
e apenas os erros ocorridos durante o processo de inicialização do mecanismo se transformavam em JsEngineLoadException
. Havia também uma classe base JsException
, da qual dois dos tipos de exceções acima foram herdados, o que tornou possível interceptar absolutamente todos os erros que ocorreram durante a operação dos mecanismos JS. Apesar das desvantagens óbvias, essa organização de exceções se encaixa bem no conceito de uma interface unificada para acessar os recursos básicos dos mecanismos JS.
Porém, com a implementação da interrupção e pré-compilação de scripts (discutirei sobre eles nas seções a seguir), surgiu a necessidade de uma nova abordagem para organizar exceções. A primeira coisa a fazer foi adicionar um novo tipo de exceção - JsInterruptedException
, necessário para notificar o usuário sobre a interrupção da execução do script. Em seguida, foi necessário dividir explicitamente todos os erros que ocorreram durante o processamento do script em dois grupos: erros de compilação (análise) e erros de tempo de execução. Também era necessário separar todos os tipos de erros específicos do Chakra e do V8, que não estavam relacionados ao processamento de scripts. Também era necessário levar em consideração a presença de uma exceção no mecanismo Jint que ocorre quando o período de tempo limite para executar um script (tempo limite) expirou. Como resultado, foi formada uma nova abordagem para a organização de exceções, que pode ser representada como a seguinte estrutura hierárquica:
JsException
JsEngineException
JsFatalException
JsScriptException
JsCompilationException
JsRuntimeException
JsInterruptedException
JsTimeoutException
JsUsageException
JsEngineNotFoundException
*
* - essa exceção não ocorre no nível do mecanismo JS, mas no nível do JavaScript Engine Switcher.
Eu acho que a hierarquia de exceções apresentada acima não precisa de comentários, porque os nomes das exceções falam por si. Com essa abordagem, obtemos não apenas mais informações sobre as causas do erro, mas também podemos lidar com mais flexibilidade com certos tipos de exceções.
Formato de mensagem de erro unificado
Outro problema com versões anteriores do JavaScript Engine Switcher foi a dificuldade em localizar erros que ocorreram durante o processamento de scripts. Na propriedade de exceção Message
, era difícil entender exatamente onde ocorreu o erro no código; portanto, tive que analisar outras propriedades de exceção, o que nem sempre era conveniente. Além disso, o conjunto de propriedades existentes também não foi suficiente.
Portanto, 2 novas propriedades foram adicionadas à classe JsScriptException
:
- Tipo - tipo de erro de JavaScript (por exemplo,
SyntaxError
ou TypeError
); - DocumentName - o nome do documento (geralmente extraído dos valores dos seguintes parâmetros:
documentName
dos métodos Execute
e Evaluate
, path
método ExecuteFile
, resourceName
método ExecuteResource
etc.);
Uma nova propriedade também foi incluída na classe JsRuntimeException
- CallStack , que contém uma representação em cadeia da pilha de chamadas.
Anteriormente, um valor de uma propriedade semelhante da exceção .NET original ou uma representação de seqüência de caracteres de um erro JavaScript era simplesmente copiado na propriedade Message
. Freqüentemente, as mensagens de erro em diferentes mecanismos JS diferiam não apenas no formato, mas também na quantidade de informações úteis apresentadas neles. Por exemplo, devido à falta de informações sobre o número de linhas e colunas em algumas mensagens de erro, os desenvolvedores da biblioteca ReactJS.NET foram forçados a lançar novamente as exceções recebidas do JavaScript Engine Switcher.
Portanto, decidi gerar minhas próprias mensagens de erro no nível do módulo adaptador, que teria um formato único (unificado). Este formato usa todas as informações de erro disponíveis: tipo, descrição, nome do documento, número da linha, número da coluna, fragmento de código e pilha de chamadas. Como base para o novo formato, peguei o formato de erro da biblioteca Microsoft ClearScript .
Abaixo estão as mensagens sobre o mesmo erro de compilação que foram geradas por diferentes módulos do adaptador:
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
Um exemplo semelhante para um erro de tempo de execução:
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
Como você pode ver nos exemplos, alguns mecanismos JS nos fornecem descrições de erro e números de coluna completamente diferentes, e nem sempre podemos obter um conjunto completo de dados de erro, mas, apesar dessas deficiências, o formato unificado nos fornece mais informações sobre o local do erro do que mensagens de erro originais.
Dicas de implantação de assembly nativo
A principal causa de erros ao trabalhar com a segunda versão do JavaScript Engine Switcher foi que muitos desenvolvedores esqueceram de instalar pacotes NuGet contendo assemblies nativos para os módulos ChakraCore e V8. Ao mesmo tempo, uma postagem no rastreador de erros ReactJS.NET também foi dedicada a esse problema ( a tradução para o russo também está disponível). Agora, esse erro é encontrado principalmente apenas por iniciantes que, por algum motivo, não leram a documentação.
Os autores do ReactJS.NET tentaram minimizar o número de erros usando as dicas contidas nas mensagens de erro, mas a implementação não muito bem-sucedida dessa abordagem levou a ainda mais confusão . A idéia de dicas era boa, mas exigia uma implementação fundamentalmente diferente, ou seja, implementação no nível dos módulos adaptadores dos mecanismos JS. Na nova versão do JavaScript Engine Switcher, essas dicas são adicionadas à mensagem de erro ao DllNotFoundException
exceções DllNotFoundException
e TypeLoadException
JsEngineLoadException
(consulte exemplos de implementação dos módulos ChakraCore , V8 e Vroom ). Além disso, essas dicas são inteligentes, porque sua geração leva em consideração vários fatores: tipo de sistema operacional, arquitetura do processador e tempo de execução (.NET Framework, .NET Core ou Mono).
Por exemplo, ao usar o módulo ChakraCore sem montagem nativa em um processo de 64 bits no sistema operacional Windows, a mensagem de erro será semelhante a esta:
Falha ao criar a instância do ChakraCoreJsEngine. Provavelmente aconteceu, porque o assembly 'ChakraCore.dll' ou uma de suas dependências não foi encontrado. Tente instalar o pacote JavaScriptEngineSwitcher.ChakraCore.Native.win-x64 via NuGet. Além disso, você ainda precisa instalar o Microsoft Visual C ++ Redistributable para Visual Studio 2017 ( https://www.visualstudio.com/downloads/#microsoft-visual-c-redistributable-for-visual-studio-2017 ).
A mensagem de erro fornece uma dica de que você precisa instalar o pacote NuGet JavaScriptEngineSwitcher.ChakraCore.Native.win-x64 e também menciona que o ChakraCore for Windows exige que o componente redistribuível do Microsoft Visual C ++ para o Visual Studio 2017 funcione. ocorrer em um processo de 32 bits, o usuário será solicitado a instalar o pacote JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.
Uma mensagem de erro semelhante no Linux no .NET Core seria assim:
Falha ao criar a instância do ChakraCoreJsEngine. Provavelmente aconteceu, porque o assembly 'libChakraCore.so' ou uma de suas dependências não foi encontrado. Tente instalar o pacote JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 via NuGet.
Nesse caso, será sugerido instalar o pacote JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64.
Quando iniciado no Mono, outro prompt será exibido:
... Os pacotes JavaScriptEngineSwitcher.ChakraCore.Native.linux- * não oferecem suporte à instalação no Mono, mas você pode instalar o assembly nativo manualmente ( https://github.com/Taritsyn/JavaScriptEngineSwitcher/wiki/ChakraCore#linux ).
Como o pacote JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 é compatível apenas com o .NET Core, a dica de ferramenta fornecerá um link para instruções para implantar manualmente o assembly nativo no Linux.
Muitos outros exemplos podem ser dados, mas isso não faz sentido.
Interrompendo a Execução de Script
Quando damos aos usuários a oportunidade de executar código JS arbitrário no servidor, somos confrontados com um problema muito sério - não sabemos quanto tempo levará para executar esse código. Pode ser uma grande quantidade de código não ideal ou código que executa um loop infinito. De qualquer forma, será um código não controlado que consumirá os recursos de nosso servidor indefinidamente. Para controlar de alguma forma esse processo, precisamos interromper a execução dos scripts. Ao usar mecanismos escritos em .NET puro (por exemplo, Jint, Jurassic ou NiL.JS), sempre podemos começar a executar o código JS como uma tarefa com a capacidade de cancelar, mas essa abordagem não funcionará para outros mecanismos. Felizmente para nós, os mecanismos C ++ possuem mecanismos internos para interromper scripts.
Para fornecer acesso a esses mecanismos, a propriedade SupportsScriptInterruption
e o método Interrupt
foram adicionados à interface IJsEngine
. Como nem todos os mecanismos oferecem suporte a esse recurso, você deve sempre verificar o valor da propriedade SupportsScriptInterruption
antes de chamar o método Interrupt
(se nas versões anteriores do JavaScript Engine Switcher você precisou executar manualmente o coletor de lixo, entenderá imediatamente do que estou falando):
if (engine.SupportsScriptInterruption) { engine.Interrupt(); }
E você precisa chamar esse método em um thread separado, diferente do segmento no qual os scripts são executados. Depois de chamar o método Interrupt
, todos os CallFunction
Evaluate
, Execute*
e CallFunction
execução anteriormente terminarão com uma JsInterruptedException
.
Como essa API é de baixo nível, é recomendável usar métodos de extensão como os seguintes para as tarefas descritas no início da seção:
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 ); } } } … } …
Este método é um complemento no método de mecanismo Evaluate<T>
, que permite usar o parâmetro timeoutInterval
definir o tempo limite para aguardar a execução do script. O princípio de operação deste método de extensão é muito simples. Primeiro, verificamos se nosso mecanismo suporta o mecanismo de interrupção interno. Se isso timeoutInterval
, criamos uma instância da classe Timer
, que, após o intervalo de timeoutInterval
especificado no parâmetro timeoutInterval
, inicia o método Interrupt
, chamamos o método de mecanismo Evaluate<T>
e, em caso de erro, capturamos o JsInterruptedException
e o envolvemos em um JsTimeoutException
. Se o mecanismo não suportar interrupções, crie uma instância da classe Task
que execute o método Evaluate<T>
, defina o intervalo de espera para que a tarefa seja executada igual ao valor do parâmetro timeoutInterval
e, se a tarefa for concluída por tempo limite, JsTimeoutException
uma JsTimeoutException
tipo JsTimeoutException
. A seguir, é apresentado um exemplo do uso desse método de extensão:
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)); } } } … } …
Usando o método de extensão, calculamos o resultado da expressão JS. O resultado da expressão é um número inteiro aleatório e esse número também é o número de segundos pelos quais essa expressão está atrasada. Além disso, ao chamar o método de extensão, especificamos um intervalo de espera de 3 segundos. Como o tempo para calcular a expressão varia de 1 a 10 segundos, em um caso, o resultado da expressão será exibido no console e, em outro, uma mensagem sobre o tempo limite.
Não deve ser difícil refazer esse método de extensão para chamar outros métodos de mecanismo (por exemplo, para Execute*
, CallFunction
ou outras versões sobrecarregadas do método Evaluate
). Até o momento, ainda não decidi se vou adicionar esses métodos de extensão à própria biblioteca, porque ainda não está claro quanto eles serão solicitados.
Pré-compilação de scripts
Me pediram para adicionar suporte à compilação preliminar de scripts em 2015 . Naquele momento, não estava claro como essa função poderia se encaixar no conceito de uma interface unificada. Mas com o passar do tempo, o suporte a recursos de acessibilidade, como coleta de lixo e interrupção de script, começou a aparecer gradualmente no JavaScript Engine Switcher. Nos últimos estágios do trabalho da terceira versão, chegou a vez da compilação preliminar.
Usando a pré-compilação, você pode compilar um script uma vez e usá-lo várias vezes para inicializar os mecanismos JS. Devido ao fato de o script pré-compilado não exigir análise, a inicialização dos mecanismos será mais rápida.
Atualmente, a pré-compilação é suportada por 5 módulos adaptadores: ChakraCore, Jint, Jurassic, MSIE (apenas nos modos JsRT) e V8. Para fornecer acesso aos mecanismos de mecanismo correspondentes, a propriedade SupportsScriptPrecompilation
e três novos métodos foram adicionados à interface IJsEngine
: IJsEngine
, IJsEngine
e PrecompileResource
. Precompile*
métodos de Precompile*
retornam uma instância de um objeto que implementa a interface IPrecompiledScript
. Este objeto é um script pré-compilado que pode ser usado por diferentes instâncias dos mecanismos (uma versão sobrecarregada do método Execute
serve para esse propósito). Considere um exemplo simples de uso desta API:
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.
Referências
- JavaScript Engine Switcher GitHub
- JavaScript Engine Switcher
- « JavaScript Engine Switcher 2.X»
- JSPool GitHub
- ReactJS.NET
- ReactJS.NET GitHub