diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..a2962bd0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,34 @@ +# Top-most EditorConfig file +root = true + +[*] +end_of_line = crlf +indent_style = tab +tab_width = 4 +trim_trailing_whitespace = true +insert_final_newline = false + +[*.{css,less}] +indent_style = space +indent_size = 2 + +[*.json] +indent_style = space +indent_size = 2 +insert_final_newline = true + +[*.{xml,config,slnx,csproj,props,targets,nuspec}] +indent_style = space +indent_size = 2 + +[*.{txt,md}] +indent_style = space +indent_size = unset + +[**/{lib,build}/**] +charset = unset +end_of_line = unset +indent_style = unset +indent_size = unset +trim_trailing_whitespace = unset +insert_final_newline = unset \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..9607807f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,19 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.slnx merge=union +*.csproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6f83734e..9dafd006 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,88 @@ +# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) +[Bb]in/ +[Oo]bj/ + +# Node.js tools +bower_components/ +node_modules/ +package-lock.json + +# BenchmarkDotNet artifacts +BenchmarkDotNet.Artifacts/ + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +.vs/ +.vscode/ +.idea/ +*.sln.docstates *.suo *.user +*.userprefs + +# Build results +artifacts/ *.FileListAbsolute.txt -bin/ -obj/ -*.ncb -*.nlb -*.aps -*.clw -*.pdb +*.ilk +*.log +*.meta *.obj *.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tlh +*.tli +*.tmp *.vspscc +*.vssscc *_i.c *_p.c -*.tlb -*.tlh + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# ReSharper is a .NET coding add-in +_ReSharper.*/ + +# Publishing +PublishProfiles/ +*.Publish.xml + +# NuGet Packages +packages/ +*.nupkg +*.snupkg + +# Others +.build/ +.nuget/ *.bak *.[Cc]ache -*.ilk -*.log -*.lib -*.sbr +*.dbmdl +*.docstates +*.orig *.scc -*.sig -_ReSharper*/ -*.orig \ No newline at end of file +*DS_Store + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog.htm +UpgradeLog*.XML + +# Current project-specific folders and files +nuget/ +**/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/lib/*.min.js +**/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/lib/ +**/scripts/build/ +**/styles/build/ +**/wwwroot/lib/ \ No newline at end of file diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe index 3ffdd33c..ec1309c7 100644 Binary files a/.nuget/NuGet.exe and b/.nuget/NuGet.exe differ diff --git a/.nuget/NuGet.targets b/.nuget/NuGet.targets index f9438127..2384765d 100644 --- a/.nuget/NuGet.targets +++ b/.nuget/NuGet.targets @@ -1,144 +1,144 @@ - - $(MSBuildProjectDirectory)\..\ - - - false - - - false - - - true - - - false - - - - - - - - - - - $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) - - - - - $(SolutionDir).nuget - - - - $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config - $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config - - - - $(MSBuildProjectDirectory)\packages.config - $(PackagesProjectConfig) - - - - - $(NuGetToolsPath)\NuGet.exe - @(PackageSource) - - "$(NuGetExePath)" - mono --runtime=v4.0.30319 $(NuGetExePath) - - $(TargetDir.Trim('\\')) - - -RequireConsent - -NonInteractive - - "$(SolutionDir) " - "$(SolutionDir)" - - - $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) - $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols - - - - RestorePackages; - $(BuildDependsOn); - - - - - $(BuildDependsOn); - BuildPackage; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + $(MSBuildProjectDirectory)\..\ + + + false + + + false + + + true + + + false + + + + + + + + + + + $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) + + + + + $(SolutionDir).nuget + + + + $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config + $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config + + + + $(MSBuildProjectDirectory)\packages.config + $(PackagesProjectConfig) + + + + + $(NuGetToolsPath)\NuGet.exe + @(PackageSource) + + "$(NuGetExePath)" + mono --runtime=v4.0.30319 $(NuGetExePath) + + $(TargetDir.Trim('\\')) + + -RequireConsent + -NonInteractive + + "$(SolutionDir) " + "$(SolutionDir)" + + + $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) + $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols + + + + RestorePackages; + $(BuildDependsOn); + + + + + $(BuildDependsOn); + BuildPackage; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Binaries/ChakraCore/x64/ChakraCore.dll b/Binaries/ChakraCore/x64/ChakraCore.dll deleted file mode 100644 index 71153885..00000000 Binary files a/Binaries/ChakraCore/x64/ChakraCore.dll and /dev/null differ diff --git a/Binaries/ChakraCore/x86/ChakraCore.dll b/Binaries/ChakraCore/x86/ChakraCore.dll deleted file mode 100644 index 24b60153..00000000 Binary files a/Binaries/ChakraCore/x86/ChakraCore.dll and /dev/null differ diff --git a/Binaries/ClearScript/ClearScript.dll b/Binaries/ClearScript/ClearScript.dll deleted file mode 100644 index f512a6dd..00000000 Binary files a/Binaries/ClearScript/ClearScript.dll and /dev/null differ diff --git a/Binaries/ClearScript/LICENSE.v8.txt b/Binaries/ClearScript/LICENSE.v8.txt deleted file mode 100644 index bf2314e3..00000000 --- a/Binaries/ClearScript/LICENSE.v8.txt +++ /dev/null @@ -1,26 +0,0 @@ -Copyright 2006-2011, the V8 project authors. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Binaries/ClearScript/MS-PL.txt b/Binaries/ClearScript/MS-PL.txt deleted file mode 100644 index 0f377af9..00000000 --- a/Binaries/ClearScript/MS-PL.txt +++ /dev/null @@ -1,56 +0,0 @@ -Microsoft Public License (MS-PL) - -This license governs use of the accompanying software. If you use the -software, you accept this license. If you do not accept the license, do not -use the software. - -1. Definitions - - The terms "reproduce," "reproduction," "derivative works," and - "distribution" have the same meaning here as under U.S. copyright law. A - "contribution" is the original software, or any additions or changes to - the software. A "contributor" is any person that distributes its - contribution under this license. "Licensed patents" are a contributor's - patent claims that read directly on its contribution. - -2. Grant of Rights - - (A) Copyright Grant- Subject to the terms of this license, including the - license conditions and limitations in section 3, each contributor - grants you a non-exclusive, worldwide, royalty-free copyright license - to reproduce its contribution, prepare derivative works of its - contribution, and distribute its contribution or any derivative works - that you create. - - (B) Patent Grant- Subject to the terms of this license, including the - license conditions and limitations in section 3, each contributor - grants you a non-exclusive, worldwide, royalty-free license under its - licensed patents to make, have made, use, sell, offer for sale, - import, and/or otherwise dispose of its contribution in the software - or derivative works of the contribution in the software. - -3. Conditions and Limitations - - (A) No Trademark License- This license does not grant you rights to use - any contributors' name, logo, or trademarks. - - (B) If you bring a patent claim against any contributor over patents that - you claim are infringed by the software, your patent license from such - contributor to the software ends automatically. - - (C) If you distribute any portion of the software, you must retain all - copyright, patent, trademark, and attribution notices that are present - in the software. - - (D) If you distribute any portion of the software in source code form, you - may do so only under this license by including a complete copy of this - license with your distribution. If you distribute any portion of the - software in compiled or object code form, you may only do so under a - license that complies with this license. - - (E) The software is licensed "as-is." You bear the risk of using it. The - contributors give no express warranties, guarantees or conditions. You - may have additional consumer rights under your local laws which this - license cannot change. To the extent permitted under your local laws, - the contributors exclude the implied warranties of merchantability, - fitness for a particular purpose and non-infringement. \ No newline at end of file diff --git a/Binaries/ClearScript/x64/ClearScriptV8-64.dll b/Binaries/ClearScript/x64/ClearScriptV8-64.dll deleted file mode 100644 index 5677870c..00000000 Binary files a/Binaries/ClearScript/x64/ClearScriptV8-64.dll and /dev/null differ diff --git a/Binaries/ClearScript/x64/v8-x64.dll b/Binaries/ClearScript/x64/v8-x64.dll deleted file mode 100644 index bf377b98..00000000 Binary files a/Binaries/ClearScript/x64/v8-x64.dll and /dev/null differ diff --git a/Binaries/ClearScript/x86/ClearScriptV8-32.dll b/Binaries/ClearScript/x86/ClearScriptV8-32.dll deleted file mode 100644 index 5269cf6e..00000000 Binary files a/Binaries/ClearScript/x86/ClearScriptV8-32.dll and /dev/null differ diff --git a/Binaries/ClearScript/x86/v8-ia32.dll b/Binaries/ClearScript/x86/v8-ia32.dll deleted file mode 100644 index 1fffa40a..00000000 Binary files a/Binaries/ClearScript/x86/v8-ia32.dll and /dev/null differ diff --git a/Binaries/Jint/Jint.dll b/Binaries/Jint/Jint.dll deleted file mode 100644 index 751b017d..00000000 Binary files a/Binaries/Jint/Jint.dll and /dev/null differ diff --git a/Binaries/Jurassic/Jurassic.dll b/Binaries/Jurassic/Jurassic.dll deleted file mode 100644 index 67f18a50..00000000 Binary files a/Binaries/Jurassic/Jurassic.dll and /dev/null differ diff --git a/CHANGELOG.md b/CHANGELOG.md index bd311199..b9dd11f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,38 +1,1103 @@ Change log ========== -## June 30, 2016 - v1.5.8 +## v3.30.2 - July 15, 2025 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 4.4.0 + * In JavaScriptEngineSwitcher.Yantra added support for the YantraJS version 1.2.286 + +## v3.30.1 - July 1, 2025 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 4.3.0 + +## v3.30.0 - June 16, 2025 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 4.2.2 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.6.1700 + * No longer supports a .NET 5 and 7 + * Added support for .NET 9 + +## v3.29.1 - March 14, 2025 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 4.2.1 + * In JavaScriptEngineSwitcher.V8 performed a migration to a modern API for pre-compilation of scripts + +## v3.29.0 - March 8, 2025 + * In JavaScriptEngineSwitcher.Jurassic added support for the Jurassic version of February 4, 2025 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 7.5 (support of the V8 version 13.3.415.23) + * No longer supports a .NET Framework 4.5 + * Added support for .NET Framework 4.6.2 + * In JavaScriptEngineSwitcher.Yantra added support for the YantraJS version 1.2.282 + +## v3.28.0 - January 29, 2025 + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 4.2.0 + * No longer supports a .NET 6 + +## v3.27.3 - December 18, 2024 + * In JavaScriptEngineSwitcher.ChakraCore added support for the ChakraCore version of August 1, 2024 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 4.1.0 + * In JavaScriptEngineSwitcher.Jurassic added support for the Jurassic version of November 22, 2024 + * In JavaScriptEngineSwitcher.Yantra added support for the YantraJS version 1.2.246 + +## v3.27.2 - September 1, 2024 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 4.0.2 + * In JavaScriptEngineSwitcher.Yantra added support for the YantraJS version 1.2.225 + +## v3.27.1 - August 20, 2024 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 4.0.1 + * In JavaScriptEngineSwitcher.Yantra added support for the YantraJS version 1.2.218 + +## v3.27.0 - July 24, 2024 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 4.0.0 + +## v3.26.1 - June 18, 2024 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.1.3 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.5.1684 + * Added support for .NET 8 + +## v3.26.0 - April 22, 2024 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version of April 21, 2024 + * In `JavaScriptEngineSwitcher.ChakraCore.Native.win-*` packages an additional deployment mechanism has been added to MSBuild scripts for the case when the target platform is focused on a specific processor architecture + +## v3.25.0 - April 10, 2024 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.1.0 + * In JavaScriptEngineSwitcher.Yantra added support for the YantraJS version 1.2.209 + +## v3.24.2 - March 25, 2024 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 7.4.5 (support of the V8 version 12.3.219.12) + * In configuration settings of the V8 JS engine was added two new properties: `AddPerformanceObject` (default `false`) and `SetTimerResolution` (default `false`) + +## v3.24.1 - March 7, 2024 + * Minor improvements in metadata of NuGet packages + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.0.1 + * In JavaScriptEngineSwitcher.Msie added support for the MSIE JavaScript Engine version 3.2.5 + +## v3.24.0 - February 6, 2024 + * In the `JsEngineSwitcher` class was added the `AllowCurrentProperty` property (default `true`) + * In JavaScriptEngineSwitcher.Extensions.MsDependencyInjection: + * `AddJsEngineSwitcher(Action)` and `AddJsEngineSwitcher(IJsEngineSwitcher, Action)` extension methods are replaced by new methods accordingly: `AddJsEngineSwitcher(Action)` and `AddJsEngineSwitcher(IJsEngineSwitcher, Action)` + * `AllowCurrentProperty` property of `JsEngineSwitcherOptions` class allows to forbid usage of the `JsEngineSwitcher.Current` property. This feature can be used to fix a [error #115](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/115) “Concurrency issue when initializing JS engine switcher in startup”. Special thanks to [Ville Häkli](https://github.com/VilleHakli) + +## v3.23.9 - January 20, 2024 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.0.0 + +## v3.23.8 - January 17, 2024 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.0.0 RC 1 + +## v3.23.7 - January 8, 2024 + * In JavaScriptEngineSwitcher.ChakraCore fixed a error that occurred in the `ReflectionHelpers.IsAllowedProperty` method when running on .NET Core 1.0 + * In JavaScriptEngineSwitcher.Msie added support for the MSIE JavaScript Engine version 3.2.4 + +## v3.23.6 - January 6, 2024 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.0.0 Beta 2059 + +## v3.23.5 - December 9, 2023 + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 3.0.0 Beta 2057 + * Added support for .NET 8 + * In JavaScriptEngineSwitcher.Msie added support for the MSIE JavaScript Engine version 3.2.3 + * In JavaScriptEngineSwitcher.NiL added support for the NiL.JS version 2.5.1677 + +## v3.23.4 - November 11, 2023 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.0.0 Beta 2055 + * In JavaScriptEngineSwitcher.Yantra added support for the YantraJS version 1.2.206 + +## v3.23.3 - November 6, 2023 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.0.0 Beta 2054 + * In JavaScriptEngineSwitcher.Jurassic added support for the Jurassic version of November 1, 2023 + * In JavaScriptEngineSwitcher.Yantra added support for the YantraJS version 1.2.204 + +## v3.23.2 - October 26, 2023 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.0.0 Beta 2053 + * In JavaScriptEngineSwitcher.NiL added support for the NiL.JS version 2.5.1674 + * In JavaScriptEngineSwitcher.V8 added support for the Microsoft ClearScript.V8 version 7.4.4 (support of the V8 version 11.8.172.15) + * In JavaScriptEngineSwitcher.Yantra added support for the YantraJS version 1.2.195 + +## v3.23.1 - September 19, 2023 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.0.0 Beta 2052 + * In JavaScriptEngineSwitcher.Node added support for the Jering.Javascript.NodeJS version 7.0.0 + * In JavaScriptEngineSwitcher.Yantra added support for the YantraJS version 1.2.188 + +## v3.23.0 - September 8, 2023 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.5.1673 + * Restored support for .NET Framework 4.6.1 + +## v3.22.0 - September 5, 2023 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.5.1672 + * No longer supports a .NET Framework 4.6.1 + +## v3.21.6 - August 30, 2023 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.0.0 Beta 2051 + +## v3.21.5 - August 21, 2023 + * In JavaScriptEngineSwitcher.V8 added support for the Microsoft ClearScript.V8 version 7.4.3 (support of the V8 version 11.6.189.18) + +## v3.21.4 - August 3, 2023 + * In JavaScriptEngineSwitcher.Jint added support for the Jint version 3.0.0 Beta 2050 + * In JavaScriptEngineSwitcher.NiL added support for the NiL.JS version 2.5.1665 + * In JavaScriptEngineSwitcher.Node added support for the Jering.Javascript.NodeJS version 7.0.0 Beta 5 + * In JavaScriptEngineSwitcher.Yantra added support for the YantraJS version 1.2.179 + +## v3.21.3 - June 1, 2023 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.4.2 (support of V8 version 11.4.183.17) + * In JavaScriptEngineSwitcher.Yantra added support of YantraJS version 1.2.163 + +## v3.21.2 - May 1, 2023 + * In JavaScriptEngineSwitcher.Jint: + * The package is no longer marked as a prerelease + * In configuration settings of the Jint JS engine was added one new property - `MaxJsonParseDepth` (default `64`) + * In JavaScriptEngineSwitcher.Node: + * Jering.Javascript.NodeJS was updated to version 7.0.0 Beta 4 + * Added support of .NET 7 + +## v3.21.1 - April 11, 2023 + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 3.0.0 Beta 2049 + * Added support of .NET 6 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.2.2 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.4.1 (support of V8 version 11.2.214.13) + * In JavaScriptEngineSwitcher.Yantra added support of YantraJS version 1.2.160 + +## v3.21.0 - April 1, 2023 + * Added a module based on the [YantraJS](https://yantrajs.com). Special thanks to [Akash Kava](https://github.com/ackava) + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version of January 26, 2023 + * In configuration settings of the ChakraCore JS engine was added one new property - `AllowReflection` (default `false`) + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 3.0.0 Beta 2048 + * In configuration settings of the Jint JS engine was added two new properties: `AllowReflection` (default `false`) and `DisableEval` (default `false`) + * In JavaScriptEngineSwitcher.Jurassic improved a conversion of results to a host types + * In JavaScriptEngineSwitcher.Msie: + * MSIE JavaScript Engine was updated to version 3.2.1 + * In configuration settings of the MSIE JS engine was added one new property - `AllowReflection` (default `false`) + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1661 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 7.4.0 (support of V8 version 11.1.277.14) + * In configuration settings of the V8 JS engine was added one new property - `AllowReflection` (default `false`) + +## v3.20.10 - January 23, 2023 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.3.7 (support of V8 version 10.9.194.10) + +## v3.20.9 - January 19, 2023 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 2046 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.5.1623 + * Added support of .NET 7.0 + +## v3.20.8 - December 20, 2022 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 2044 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.3.6 (support of V8 version 10.8.168.24) + +## v3.20.7 - November 13, 2022 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.3.5 (support of V8 version 10.7.193.22) + +## v3.20.6 - November 11, 2022 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version of November 9, 2022 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 2043 + +## v3.20.5 - October 12, 2022 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version of October 7, 2022 + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of September 20, 2022 + +## v3.20.4 - September 30, 2022 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.3.4 (support of V8 version 10.6.194.14) + +## v3.20.3 - September 28, 2022 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.3.3 (support of V8 version 10.6.194.14) + +## v3.20.2 - September 16, 2022 + * In JavaScriptEngineSwitcher.Jint the implementation of script interruption has been refactored + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 7.3.2 (support of V8 version 10.5.218.8) + * In configuration settings of the V8 JS engine was added one new property - `DisableDynamicBinding` (default `false`) + +## v3.20.1 - September 11, 2022 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 2041 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.5.1600 + * JS run-time exception now contains a script call stack + +## v3.20.0 - August 31, 2022 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.5.1591 + * No longer supports a .NET Standard + +## v3.19.1 - August 28, 2022 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 2040 + +## v3.19.0 - July 21, 2022 + * Fixed a [error #102](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/102) “Resources should conform to correct ICU standard for naming”. Special thanks to [Tim Heuer](https://github.com/timheuer) + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 3.0.0 Beta 2039 + * No longer supports a .NET Framework 4.6.1 + * Added support of .NET Framework 4.6.2 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.9 + +## v3.18.4 - June 29, 2022 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.3.1 (support of V8 version 10.3.174.17) + +## v3.18.3 - June 3, 2022 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.3.0 (support of V8 version 10.2.154.5) + +## v3.18.2 - May 24, 2022 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version of January 31, 2022 + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1560 + * In JavaScriptEngineSwitcher.Node added support of Jering.Javascript.NodeJS version 6.3.1 + +## v3.18.1 - May 3, 2022 + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of April 30, 2022 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.2.5 (support of V8 version 10.1.124.11) + +## v3.18.0 - April 21, 2022 + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 3.0.0 Beta 2038 + * In configuration settings of the Jint JS engine were changed types of the `DebuggerBreakCallback` and `DebuggerStepCallback` properties to the + DebugEventHandler type + +## v3.17.4 - March 30, 2022 + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of March 12, 2022 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.2.4 (support of V8 version 10.0.139.8) + +## v3.17.3 - March 6, 2022 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.2.3 (support of V8 version 9.9.115.8) + +## v3.17.2 - February 7, 2022 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.2.2 (support of V8 version 9.8.177.9) + +## v3.17.1 - January 11, 2022 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.2.1 (support of V8 version 9.7.106.18) + +## v3.17.0 - December 27, 2021 + * In JavaScriptEngineSwitcher.Node: + * Jering.Javascript.NodeJS was updated to version 6.3.0 + * Added support of .NET 6 + +## v3.16.0 - December 9, 2021 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version of November 11, 2021 + * No longer supports a .NET Core App 2.1 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 2037 + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1552 + +## v3.15.0 - November 28, 2021 + * In JavaScriptEngineSwitcher.Node: + * Jering.Javascript.NodeJS was updated to version 6.2.0 + * Added support of .NET Core App 3.1 and .NET 5.0 + +## v3.14.0 - November 23, 2021 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 2036 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.5.1541 + * No longer supports a .NET Framework 4.0 Client and .NET Framework 4.5 + * Added support of .NET Framework 4.8 and .NET 6.0 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.2.0 (support of V8 version 9.6.180.14) + +## v3.13.3 - October 21, 2021 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.1.7 (support of V8 version 9.5.172.21) + +## v3.13.2 - October 6, 2021 + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 3.0.0 Beta 2035 + * In configuration settings of the Jint JS engine was added one new property - `MaxArraySize` (default `uint.MaxValue`) + * In JavaScriptEngineSwitcher.V8 implemented a handling of error “Internal error. Icu error” + +## v3.13.1 - September 22, 2021 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 2034 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.1.6 (support of V8 version 9.4.146.16) + +## v3.13.0 - August 19, 2021 + * In JavaScriptEngineSwitcher.Jurassic: + * Jurassic was updated to version of August 18, 2021 + * Debugging is no longer supported + +## v3.12.6 - August 10, 2021 + * In JavaScriptEngineSwitcher.ChakraCore: + * Improved a implementation of the `Dispose` method + * MSBuild and PowerShell scripts for installing the native assemblies are made more simple and universal + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.8 + +## v3.12.5 - July 23, 2021 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 2033 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 7.1.5 (support of V8 version 9.2.230.21) + * In configuration settings of the V8 JS engine was added one new property - `MaxArrayBufferAllocation` (default `UInt64.MaxValue`) + +## v3.12.4 - June 29, 2021 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.5.1514 + * Added support of .NET Core App 3.1 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 7.1.4 (support of V8 version 9.1.269.36) + * Added support of OS X (ARM64) + +## v3.12.3 - May 26, 2021 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.1.3 (support of V8 version 9.1.269.28) + +## v3.12.2 - May 20, 2021 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 2032 + +## v3.12.1 - April 18, 2021 + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1493 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.1.2 (support of V8 version 9.0.257.19) + +## v3.12.0 - March 28, 2021 + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of March 19, 2021 + +## v3.11.4 - March 19, 2021 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.5.1486 + * Added support of .NET 5.0 + +## v3.11.3 - March 9, 2021 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 2031 + +## v3.11.2 - March 5, 2021 + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1475 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 7.1.1 (support of V8 version 8.9.255.20) + * Added support of .NET Standard 2.1 + * Added support of Linux (ARM) + * In configuration settings of the V8 JS engine was added one new property - `HeapExpansionMultiplier` (default `0`) + +## v3.11.1 - February 1, 2021 + * In JavaScriptEngineSwitcher.Jint: + * Runtime exceptions now contain a stack trace + * In configuration settings of the Jint JS engine a `AllowDebuggerStatement` property has been returned so as not to break compatibility with previous versions + +## v3.11.0 - January 30, 2021 + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 3.0.0 Beta 2002 + * In configuration settings of the Jint JS engine a `AllowDebuggerStatement` property has been replaced by the `DebuggerStatementHandlingMode` property (default `Ignore`) and was added two new properties: `DebuggerBreakCallback` (default `null`) and `DebuggerStepCallback` (default `null`) + +## v3.10.0 - January 22, 2021 + * In JavaScriptEngineSwitcher.Node added support of Jering.Javascript.NodeJS version 5.4.4 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 7.1 (support of V8 version 8.8.278.14) + * Added support of Windows (ARM64) and Linux (ARM64) + +## v3.9.1 - December 9, 2020 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.24 ([Tomáš Deml's patch](https://github.com/microsoft/ChakraCore/issues/5973) applied) + +## v3.9.0 - November 19, 2020 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 7.0 (support of V8 version 8.7.220.25) + * Added support of .NET Framework 4.7.1 and .NET 5.0 + * Added support of Linux (x64) and OS X (x64) + * Own versions of the ClearScript's assemblies are no longer build, because the [official NuGet package](https://www.nuget.org/packages/Microsoft.ClearScript.V8) is now used. Therefore, you should also replace in your projects the `JavaScriptEngineSwitcher.V8.Native.*` packages by the `Microsoft.ClearScript.V8.Native.*` packages. + +## v3.8.5 - November 11, 2020 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.23 ([Tomáš Deml's patch](https://github.com/microsoft/ChakraCore/issues/5973) applied) + +## v3.8.4 - November 7, 2020 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 1914 + +## v3.9.0 Preview 3 - November 6, 2020 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 7.0 RC5 (support of V8 version 8.7.220.16) + * Own versions of the ClearScript's assemblies are no longer build, because the [official NuGet package](https://www.nuget.org/packages/Microsoft.ClearScript.V8) is now used. Therefore, you should also replace in your projects the `JavaScriptEngineSwitcher.V8.Native.*` packages by the `Microsoft.ClearScript.V8.Native.*` packages. + +## v3.9.0 Preview 2 - October 30, 2020 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.0 RC4 (support of V8 version 8.7.220.10) + +## v3.8.3 - October 29, 2020 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.0 RC4 (support of V8 version 8.7.220.10) + +## v3.9.0 Preview - October 24, 2020 + * In JavaScriptEngineSwitcher.V8: + * Cross-platform is implemented by using an [unofficial experimental version of the Microsoft ClearScript.V8](https://github.com/Taritsyn/ClearScript-Experimental) library, which is not targeted at any particular operating system or processor architecture, and can work with various native assemblies + * Added a packages, that contains a native assemblies for Linux (x64) and OS X (x64) + +## v3.8.2 - October 23, 2020 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.0 RC3 + +## v3.8.1 - October 21, 2020 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 7.0 RC2 + +## v3.8.0 - October 19, 2020 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 7.0 RC (support of V8 version 8.6.395.17) + * MSVC runtime was embedded into the native assemblies for Windows + +## v3.7.2 - September 9, 2020 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.11.22 ([Tomáš Deml's patch](https://github.com/microsoft/ChakraCore/issues/5973) applied) + * Added a experimental support of Windows (ARM64) + * In JavaScriptEngineSwitcher.Node added support of Jering.Javascript.NodeJS version 5.4.3 + +## v3.7.1 - August 12, 2020 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.21 ([Tomáš Deml's patch](https://github.com/microsoft/ChakraCore/issues/5973) applied) + +## v3.7.0 - August 6, 2020 + * In JavaScriptEngineSwitcher.Jurassic: + * Jurassic was updated to version of August 3, 2020 + * In configuration settings of the Jurassic JS engine was added one new non-standard property - `EnableHostCollectionsEmbeddingByValue` (default `false`) + +## v3.6.0 - August 1, 2020 + * In JavaScriptEngineSwitcher.Jint added a ability to interrupt execution of the script + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1466 + * In JavaScriptEngineSwitcher.Node added support of Jering.Javascript.NodeJS version 5.4.2 + +## v3.5.6 - June 11, 2020 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.20 ([Tomáš Deml's patch](https://github.com/microsoft/ChakraCore/issues/5973) applied) + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 1828 + +## v3.5.5 - May 29, 2020 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 6.0.2 (support of V8 version 8.3.110.9) + +## v3.5.4 - May 13, 2020 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.19 ([Tomáš Deml's patch](https://github.com/microsoft/ChakraCore/issues/5973) applied) + +## v3.5.3 - May 8, 2020 + * In JavaScriptEngineSwitcher.V8 fixed a error that caused incorrect generation of error description for an `JsEngineLoadException` exception + +## v3.5.2 - April 15, 2020 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.18 ([Tomáš Deml's patch](https://github.com/microsoft/ChakraCore/issues/5973) applied) + +## v3.5.1 - April 14, 2020 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 1778 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 6.0.1 (support of V8 version 8.1.307.28) + +## v3.5.0 - April 3, 2020 + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 3.0.0 Beta 1756 + * No longer supports a .NET Framework 4.5 + * Added support of .NET Framework 4.6.1 and .NET Standard 2.1 + +## v3.4.5 - April 1, 2020 + * In JavaScriptEngineSwitcher.ChakraCore applied the Tomáš Deml's patch to fix the [“Incompatibility in handling of SIGSEGV between ChakraCore and CoreCLR”](https://github.com/microsoft/ChakraCore/issues/5973) error + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1440 + * In JavaScriptEngineSwitcher.Node added a handling of errors that occur when switching to multi-process mode of the Jering.Javascript.NodeJS library + +## v3.4.4 - March 11, 2020 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.17 + * In JavaScriptEngineSwitcher.Node added support of Jering.Javascript.NodeJS version 5.4.0 + +## v3.4.3 - March 8, 2020 + * In JavaScriptEngineSwitcher.ChakraCore fixed a [error #82](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/82) “Program crash after function call with too much parameters” + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1431 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.7 + +## v3.4.2 - March 2, 2020 + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 3.0.0 Beta 1715 + * Simplified and improved a handling of JS runtime errors + +## v3.4.1 - February 26, 2020 + * In JavaScriptEngineSwitcher.Node the default Node JS service is now implemented as a wrapper around the StaticNodeJSService class + +## v3.4.0 - February 24, 2020 + * Added a module based on the [Jering.Javascript.NodeJS](https://github.com/JeringTech/Javascript.NodeJS) + +## v3.3.3 - February 15, 2020 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.5.1428 + * NiL.JS.NetCore package is no longer used + * Added support of .NET Framework 4.6.1 and .NET Standard 1.6 + +## v3.3.2 - February 14, 2020 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.16 + +## v3.3.1 - February 1, 2020 + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1403 + * Added a `ClearScript.xml` files to the JavaScriptEngineSwitcher.V8 package + +## v3.3.0 - December 27, 2019 + * Enabled a SourceLink in NuGet packages + * In JavaScriptEngineSwitcher.ChakraCore added support of .NET Standard 2.1 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 6.0.0 (support of V8 version 7.9.317.32) + * Added support of .NET Core 3.1 on Windows + * Now the Microsoft ClearScript.V8 requires the [Microsoft Visual C++ Redistributable for Visual Studio 2019](https://visualstudio.microsoft.com/downloads/#microsoft-visual-c-redistributable-for-visual-studio-2019) + +## v3.2.4 - December 17, 2019 + * In JavaScriptEngineSwitcher.ChakraCore fixed a errors leading to null reference exceptions in the `ReflectionHelpers` class. Special thanks to [Vanjoge](https://github.com/vanjoge). + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.6 + +## v3.2.3 - November 15, 2019 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.15 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 1632 + * In JavaScriptEngineSwitcher.NiL: + * NiL.JS was updated to version 2.5.1388 + * In configuration settings of the NiL JS engine was added one new property - `LocalTimeZone` (default `TimeZoneInfo.Local`) + +## v3.2.2 - October 26, 2019 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 3.0.0 Beta 1629 + +## v3.2.1 - October 21, 2019 + * In JavaScriptEngineSwitcher.ChakraCore: + * Fixed a error that caused a crash during finalization + * During calling of the `CollectGarbage` method is again not performed blocking + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.5 + +## v3.2.0 - October 12, 2019 + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 3.0.0 Beta 1612. Special thanks to [Marko Lahma](https://github.com/lahma) and [Sébastien Ros](https://github.com/sebastienros) + * No longer supports a .NET Framework 4.0 Client and .NET Standard 1.3 + * In configuration settings of the Jint JS engine was added two new properties: `MemoryLimit` (default `0`) and `RegexTimeoutInterval` (default `null`) + * To install this package, the “Include Prerelease” option must be set in the NuGet Package Manager + +## v3.1.10 - October 9, 2019 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.11.14 + * Slightly improved performance + * The `CollectGarbage` method is called synchronously again + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.4 + +## v3.1.9 - September 17, 2019 + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1372 + +## v3.1.8 - September 12, 2019 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.13 + +## v3.1.7 - August 14, 2019 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.11.12 + * MSVC runtime was embedded into the native assemblies for Windows + +## v3.1.6 - August 5, 2019 + * In JavaScriptEngineSwitcher.V8 fixed a [error #73](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/73) “Trying to Run this in the GAC” + +## v3.1.5 - August 2, 2019 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.6.0 (support of V8 version 7.6.303.28) + +## v3.1.4 - July 17, 2019 + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1339 + +## v3.1.3 - July 10, 2019 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.11 + +## v3.1.2 - June 13, 2019 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.10 + +## v3.1.1 - May 15, 2019 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.9 + +## v3.1.0 - May 3, 2019 + * In the `JsEngineFactoryCollection` class was added a `Count` property and `GetRegisteredFactories` method + * Removed a deprecated packages: JavaScriptEngineSwitcher.ChakraCore.Native.win8-arm and JavaScriptEngineSwitcher.ChakraCore.Native.debian-x64 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.5.6 (support of V8 version 7.4.288.26) + +## v3.0.10 - April 29, 2019 + * In JavaScriptEngineSwitcher.ChakraCore in configuration settings was changed default value of `DisableFatalOnOOM` property from `false` to `true` + * In JavaScriptEngineSwitcher.Jint fixed a error that occurred during the recursive execution and evaluation of JS files + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.3 + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1327 + +## v3.0.9 - April 10, 2019 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.8 + +## v3.0.8 - April 9, 2019 + * In JavaScriptEngineSwitcher.ChakraCore fixed a [error #72](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/72) “(chakra) recursive evaluation” + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1320 + +## v3.0.7 - March 13, 2019 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.11.7 + * Fixed a [error #68](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/68) “Embedded delegates are no longer linked with the `Function` prototype” + * In JavaScriptEngineSwitcher.Jint fixed a error that occurs in the strict mode when generating an error message + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.2 + +## v3.0.6 - February 15, 2019 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.6 + +## v3.0.5 - February 8, 2019 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.5.5 (support of V8 version 7.2.502.25) + +## v3.0.4 - February 1, 2019 + * In JavaScriptEngineSwitcher.ChakraCore in error message fixed a link to the documentation + +## v3.0.3 - January 29, 2019 + * In JavaScriptEngineSwitcher.ChakraCore improved a performance of the `UnicodeToAnsi` method of `EncodingHelpers` class + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1294 + +## v3.0.2 - January 24, 2019 + * In JavaScriptEngineSwitcher.ChakraCore: + * Reduced a memory consumption in cases, where not used the embedding of objects and types + * Fixed a wrong implementation of destruction of the embedded delegates + * Accelerated a conversion of script types to host types + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.1 + +## v3.0.1 - January 11, 2019 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.11.5 + * Fixed a [error #65](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/65) “Memory leak using EmbedHostObject” + +## v3.0.0 - December 25, 2018 + * In the `JsEngineSwitcher` class a `Instance` property was renamed to the `Current` property + * Now you can implement your own version of the `JsEngineSwitcher` class + * Format of the error messages was unified + * Created a new exception classes: `JsCompilationException`, `JsEngineException`, `JsFatalException`, `JsInterruptedException`, `JsTimeoutException`, `JsScriptException` and `JsUsageException`. These exceptions are responsible for handling errors, some of which were previously handled by the `JsRuntimeException` class + * In the `JsException` class was added two new properties: `Category` and `Description` + * From the `JsRuntimeException` class was removed one property - `ErrorCode` + * In the `JsRuntimeException` class was added three new properties: `Type`, `DocumentName` and `CallStack` + * `JsEngineLoadException` class now is inherited from the `JsEngineException` class + * Removed a `EmptyValueException` class + * `Format` method of the `JsErrorHelpers` class was renamed to the `GenerateErrorDetails` + * Part of the auxiliary code was moved to external libraries: [PolyfillsForOldDotNet](https://github.com/Taritsyn/PolyfillsForOldDotNet) and [AdvancedStringBuilder](https://github.com/Taritsyn/AdvancedStringBuilder) + * In `IJsEngine` interface was added two new properties: `SupportsScriptInterruption` and `SupportsScriptPrecompilation`, and four new methods: `Interrupt`, `Precompile`, `PrecompileFile` and `PrecompileResource` + * In JavaScriptEngineSwitcher.Extensions.MsDependencyInjection added a overloaded versions of the `AddJsEngineSwitcher` extension method, which takes an instance of JS engine switcher + * In JavaScriptEngineSwitcher.ChakraCore, JavaScriptEngineSwitcher.Msie, JavaScriptEngineSwitcher.V8 and JavaScriptEngineSwitcher.Vroom added a ability to interrupt execution of the script + * In JavaScriptEngineSwitcher.ChakraCore, JavaScriptEngineSwitcher.Jint, JavaScriptEngineSwitcher.Jurassic, JavaScriptEngineSwitcher.Msie and JavaScriptEngineSwitcher.V8 added a ability to pre-compile scripts + * In all modules, except the JavaScriptEngineSwitcher.V8, added support of .NET Standard 2.0 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.11.4 + * No longer used the old ChakraCore API for Windows (Internet Explorer-like API) + * Now the ChakraCore for Windows requires the [Microsoft Visual C++ Redistributable for Visual Studio 2017](https://www.visualstudio.com/downloads/#microsoft-visual-c-redistributable-for-visual-studio-2017) + * In configuration settings of the ChakraCore JS engine was added one new property - `MaxStackSize` (default `492` or `984` KB) + * Added support of .NET Framework 4.7.1 and .NET Core App 2.1 + * In JavaScriptEngineSwitcher.Jint: + * Jint was updated to version 2.11.58 + * In configuration settings of the Jint JS engine a `Timeout` property has been replaced by the `TimeoutInterval` property (default `TimeSpan.Zero`) and was added one new property - `LocalTimeZone` (default `TimeZoneInfo.Local`) + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of February 24, 2018 + * In JavaScriptEngineSwitcher.Msie: + * MSIE JavaScript Engine was updated to version 3.0.0 + * In configuration settings of the MSIE JS engine was added one new property - `MaxStackSize` (default `492` or `984` KB) + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 5.5.4 (support of V8 version 7.0.276.42) + * Now requires .NET Framework 4.5 or higher + * Now the Microsoft ClearScript.V8 requires the [Microsoft Visual C++ Redistributable for Visual Studio 2017](https://www.visualstudio.com/downloads/#microsoft-visual-c-redistributable-for-visual-studio-2017) + * In configuration settings of the V8 JS engine became obsolete the `MaxExecutableSize` property and added two new properties: `AwaitDebuggerAndPauseOnStart` (default `false`) and `EnableRemoteDebugging` (default `false`) + * In JavaScriptEngineSwitcher.Vroom added support of .NET Framework 4.7.1 + * Added a module based on the [NiL.JS](https://github.com/nilproject/NiL.JS) + +## v3.0.0 RC 3 - December 18, 2018 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.11.4 + * Improved a performance of script pre-compilation + +## v3.0.0 RC 2 - December 4, 2018 + * `GetSourceFragmentFromCode` and `GetSourceFragmentFromLine` methods of `JsErrorHelpers` class were replaced by the `GetTextFragment` and `GetTextFragmentFromLine` methods of `TextHelpers` class + * Part of the auxiliary code was moved to external libraries: [PolyfillsForOldDotNet](https://github.com/Taritsyn/PolyfillsForOldDotNet) and [AdvancedStringBuilder](https://github.com/Taritsyn/AdvancedStringBuilder) + * Removed a unnecessary `net471` targets + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.0 RC 2 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.5.4 (support of V8 version 7.0.276.42) + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.11.3 + * In the `NativeMethods` class for the `netstandard` and `netcoreapp` targets was changed a calling convention from `StdCall` to `Cdecl` + * Charset was explicitly specified in the `JsCreateString`, `JsCopyString` and `JsCreatePropertyId` methods of `NativeMethods` class + * Added a `netcoreapp2.1` target + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1282 + +## v2.4.29 - November 20, 2018 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.2.10 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.3 + +## v2.4.28 - October 13, 2018 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.2 + +## v2.4.27 - September 21, 2018 + * In JavaScriptEngineSwitcher.ChakraCore was optimized a memory usage in version for Unix + +## v3.0.0 RC 1 - September 19, 2018 + * Fixed a [error #59](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/59) “Unhandled exception when JS exception is thrown” + * In JavaScriptEngineSwitcher.Msie: + * Added support of MSIE JavaScript Engine version 3.0.0 RC 1 + * In configuration settings of the MSIE JS engine was added one new property - `MaxStackSize` (default `492` or `984` KB) + * In JavaScriptEngineSwitcher.Jint in configuration settings was added one new property - `LocalTimeZone` (default `TimeZoneInfo.Local`) + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.11.1 + * No longer used the old ChakraCore API for Windows (Internet Explorer-like API) + * Optimized a memory usage + * `MaxStackSize` configuration property was removed from the version for .NET Standard 1.3 + +## v2.4.26 - September 13, 2018 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.1 + +## v2.4.25 - September 8, 2018 + * In JavaScriptEngineSwitcher.Jint in configuration settings was added one new property - `LocalTimeZone` (default `TimeZoneInfo.Local`) + * In JavaScriptEngineSwitcher.ChakraCore improved a performance in version for Unix + +## v2.4.24 - August 30, 2018 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.11.0 + +## v3.0.0 Beta 9 - August 21, 2018 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.5.3 (support of V8 version 6.8.275.28) + * In JavaScriptEngineSwitcher.Jurassic the original library was rolled back to version of February 24, 2018 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.10.2 + * In JavaScriptEngineSwitcher.NiL: + * Added support of NiL.JS version 2.5.1278 + * Fixed a [error #57](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/57) “Nil - can't locate function name” + +## v2.4.23 - August 20, 2018 + * In JavaScriptEngineSwitcher.Jurassic the original library was rolled back to version of February 24, 2018 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.10.2 + +## v2.4.22 - July 11, 2018 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.10.1 + +## v3.0.0 Beta 8 - July 7, 2018 + * ChakraCore was updated to version 1.10.0 + * In configuration settings of the ChakraCore JS engine was added one new property - `DisableExecutablePageAllocation` (default `false`) + +## v2.4.21 - July 2, 2018 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.10.0 + * In configuration settings of the ChakraCore JS engine was added one new property - `DisableExecutablePageAllocation` (default `false`) + +## v3.0.0 Beta 7 - June 18, 2018 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.8.5 + +## v2.4.20 - June 13, 2018 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.8.5 + +## v3.0.0 Beta 6 - June 12, 2018 + * In JavaScriptEngineSwitcher.Extensions.MsDependencyInjection added a overloaded versions of the `AddJsEngineSwitcher` extension method, which takes an instance of JS engine switcher + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of June 7, 2018 + +## v2.4.19 - June 12, 2018 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.2.9 + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of June 7, 2018 + * In JavaScriptEngineSwitcher.ChakraCore changed a implementation of the `Dispose` method + +## v3.0.0 Beta 5 - June 6, 2018 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.0 Beta 4 + * In JavaScriptEngineSwitcher.ChakraCore: + * Changed a implementation of the `Dispose` method + * Prevented a early destruction of delegates, which have been passed to the native methods + * In JavaScriptEngineSwitcher.NiL added support of NiL.JS version 2.5.1275 + +## v3.0.0 Beta 4 - May 29, 2018 + * Fixed a [error #54](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/54) “System.Runtime.InteropServices.RuntimeInformation not required for .NET Framework 4.7.1”. Special thanks to [David Gardiner](https://github.com/flcdrg). + * Now all modules are support of .NET Framework 4.7.1 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.0 Beta 3 + * In JavaScriptEngineSwitcher.ChakraCore: + * Fixed a implementation of the `JsSerializedLoadScriptCallback` delegate + * Fixed a [error #34](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/34) “Finalazier thread is blocked because of JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine” + +## v2.4.18 - May 24, 2018 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.2.8 + * In JavaScriptEngineSwitcher.ChakraCore fixed a [error #34](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/34) “Finalazier thread is blocked because of JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine” + +## v3.0.0 Beta 3 - May 22, 2018 + * In `IJsEngine` interface was added `SupportsScriptPrecompilation` property and three new methods: `Precompile`, `PrecompileFile` and `PrecompileResource` + * In JavaScriptEngineSwitcher.Msie, JavaScriptEngineSwitcher.V8, JavaScriptEngineSwitcher.Jurassic, JavaScriptEngineSwitcher.Jint and JavaScriptEngineSwitcher.ChakraCore added a ability to pre-compile scripts + * Added a module based on the NiL.JS + * In JavaScriptEngineSwitcher.V8.Native.win-* and JavaScriptEngineSwitcher.ChakraCore.Native.win-* packages the directories with `win7-*` RIDs was renamed to `win-*` + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.0 Beta 2 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.8.4 + * JavaScriptEngineSwitcher.ChakraCore.Native.win8-arm package has been replaced by the JavaScriptEngineSwitcher.ChakraCore.Native.win-arm package + +## v2.4.17 - May 9, 2018 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.8.4 + * JavaScriptEngineSwitcher.ChakraCore.Native.win8-arm package has been replaced by the JavaScriptEngineSwitcher.ChakraCore.Native.win-arm package + +## v2.4.16 - April 13, 2018 + * In JavaScriptEngineSwitcher.V8.Native.win-* and JavaScriptEngineSwitcher.ChakraCore.Native.win-* packages the directories with `win7-*` RIDs was renamed to `win-*` + +## v3.0.0 Beta 2 - April 12, 2018 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.8.3 + +## v2.4.15 - April 11, 2018 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.2.7 + * In JavaScriptEngineSwitcher.ChakraCore and JavaScriptEngineSwitcher.Vroom fixed a minor errors + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.8.3 + +## v3.0.0 Beta 1 - April 8, 2018 + * Format of the error messages was unified + * Created a new exception classes: `JsCompilationException`, `JsEngineException`, `JsFatalException`, `JsTimeoutException` and `JsUsageException`. These exceptions are responsible for handling errors, some of which were previously handled by the `JsRuntimeException` class. + * In the `JsException` class was added two new properties: `Category` and `Description` + * From the `JsRuntimeException` class was removed one property - `ErrorCode` + * In the `JsRuntimeException` class was added three new properties: `Type`, `DocumentName` and `CallStack` + * `JsScriptInterruptedException` class was renamed to the `JsInterruptedException` class and now is inherited from the `JsRuntimeException` class + * `JsEngineLoadException` class now is inherited from the `JsEngineException` class + * Removed a `EmptyValueException` class + * `Format` method of the `JsErrorHelpers` class was renamed to the `GenerateErrorDetails` + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.0 Beta 1 + * In JavaScriptEngineSwitcher.V8: + * Added support of Microsoft ClearScript.V8 version 5.5.2 (support of V8 version 6.5.254.41) + * Now the Microsoft ClearScript.V8 requires the [Microsoft Visual C++ Redistributable for Visual Studio 2017](https://www.visualstudio.com/downloads/#microsoft-visual-c-redistributable-for-visual-studio-2017) + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of February 24, 2018 + * In JavaScriptEngineSwitcher.Jint in configuration settings of the Jint JS engine a `Timeout` property has been replaced by the `TimeoutInterval` property (default `TimeSpan.Zero`) + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.8.2 + * Now the ChakraCore for Windows requires the [Microsoft Visual C++ Redistributable for Visual Studio 2017](https://www.visualstudio.com/downloads/#microsoft-visual-c-redistributable-for-visual-studio-2017) + * In configuration settings of the ChakraCore JS engine was added one new property - `MaxStackSize` (default `492` or `984` KB) + +## v2.4.14 - March 14, 2018 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.8.2 + +## v2.4.13 - February 24, 2018 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.2.6 + * In JavaScriptEngineSwitcher.V8: + * Microsoft ClearScript.V8 was updated to version 5.4.10 + * Improved implementation of the `CallFunction` method + * Removed unnecessary locks from the `V8JsEngine` class + * In configuration settings of the V8 JS engine was added 3 new properties: `HeapSizeSampleInterval` (default `TimeSpan.Zero`), `MaxHeapSize` (default `UIntPtr.Zero`) and `MaxStackUsage` (default `UIntPtr.Zero`) + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of February 24, 2018 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 2.11.58 + +## v2.4.12 - February 20, 2018 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.8.1 + * JavaScriptEngineSwitcher.ChakraCore.Native.debian-x64 package has been replaced by the JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 package + * ICU-57 library was embedded into the `libChakraCore.so` and `libChakraCore.dylib` assemblies + * Prevented an occurrence of the “Host may not have set any promise continuation callback. Promises may not be executed.” error + * In configuration settings of the ChakraCore JS engine was added two new properties - `MemoryLimit` and `DisableFatalOnOOM` (default `false`) + * Now during calling of the `CollectGarbage` method is no longer performed blocking + +## v3.0.0 Alpha 10 - January 3, 2018 + * In JavaScriptEngineSwitcher.V8: + * Added support of Microsoft ClearScript.V8 version 5.5.1.1 (support of V8 version 6.3.292.48) + * In configuration settings of the V8 JS engine was added one new property - `AwaitDebuggerAndPauseOnStart` (default `false`) + +## v2.4.11 - December 24, 2017 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.2.5 + * In JavaScriptEngineSwitcher.ChakraCore fixed a error, that occurred during finding the suitable method overload, that receives numeric values and interfaces as parameters, of the host object + +## v3.0.0 Alpha 9 - December 22, 2017 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.0 Alpha 3 + * In JavaScriptEngineSwitcher.V8 in configuration settings of the V8 JS engine was changed types of `MaxHeapSize` and `MaxStackUsage` properties from `ulong` to `UIntPtr` + * In JavaScriptEngineSwitcher.Jint added support of Jint version 2.11.58 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.7.5 + * In configuration settings of the ChakraCore JS engine was added two new properties - `MemoryLimit` and `DisableFatalOnOOM` (default `false`) + * Now during calling of the `CollectGarbage` method is no longer performed blocking + +## v3.0.0 Alpha 8 - November 17, 2017 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 3.0.0 Alpha 2 + * In JavaScriptEngineSwitcher.ChakraCore fixed a error, that occurred during finding the suitable method overload, that receives numeric values and interfaces as parameters, of the host object + +## v3.0.0 Alpha 7 - November 12, 2017 + * In JavaScriptEngineSwitcher.V8.Native.win-* and JavaScriptEngineSwitcher.ChakraCore.Native.win* packages fixed a error “When using PackageReference DLL is not copied” + * In JavaScriptEngineSwitcher.V8: + * Added support of Microsoft ClearScript.V8 version 5.5.0 (support of V8 version 6.2.414.40) + * Now requires .NET Framework 4.5 or greater + * In configuration settings of the V8 JS engine became obsolete the `MaxExecutableSize` property and was added 4 new properties: `EnableRemoteDebugging` (default `false`), `HeapSizeSampleInterval` (default `TimeSpan.Zero`), `MaxHeapSize` (default `0`) and `MaxStackUsage` (default `0`) + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of November 2, 2017 + +## v3.0.0 Alpha 6 - October 17, 2017 + * In all modules, except the JavaScriptEngineSwitcher.V8 module, added support of .NET Standard 2.0 + * In JavaScriptEngineSwitcher.V8 improved implementation of the `CallFunction` method + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of September 1, 2017 + * In JavaScriptEngineSwitcher.ChakraCore: + * ChakraCore was updated to version 1.7.3 + * JavaScriptEngineSwitcher.ChakraCore.Native.debian-x64 package has been replaced by the JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 package + * ICU-57 library was embedded into the `libChakraCore.so` and `libChakraCore.dylib` assemblies + +## v3.0.0 Alpha 5 - September 29, 2017 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.4.10 + +## v3.0.0 Alpha 4 - September 22, 2017 + * In JavaScriptEngineSwitcher.ChakraCore prevented an occurrence of the “Host may not have set any promise continuation callback. Promises may not be executed.” error + +## v3.0.0 Alpha 3 - September 15, 2017 + * In JavaScriptEngineSwitcher.Msie: + * Added support of MSIE JavaScript Engine version 3.0.0 Alpha 1 + * Added a ability to interrupt execution of the script + * In JavaScriptEngineSwitcher.Jint added support of Jint version 2.11.23 + * In JavaScriptEngineSwitcher.ChakraCore: + * Added support of ChakraCore version 1.7.2 + * Compilation error messages now contains a information about the error location + +## v3.0.0 Alpha 2 - July 26, 2017 + * In `JsEngineSwitcher` class `Instance` property was renamed to the `Current` property + * Now you can implement your own version of the `JsEngineSwitcher` class + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of July 13, 2017 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.7.0 + +## v3.0.0 Alpha 1 - July 12, 2017 + * In `IJsEngine` interface was added `SupportsScriptInterruption` property and `Interrupt` method + * In JavaScriptEngineSwitcher.V8 and JavaScriptEngineSwitcher.ChakraCore added a ability to interrupt execution of the script + +## v2.4.10 - July 4, 2017 + * Now during the rethrowing of exceptions are preserved the full call stack trace + * In JavaScriptEngineSwitcher.ChakraCore was reduced a number of delegate-wrappers + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.2.3 + +## v2.4.9 - June 28, 2017 + * Added support of identifier names compliant with ECMAScript 5 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.2.2 + +## v2.4.8 - June 27, 2017 + * In JavaScriptEngineSwitcher.ChakraCore an attempt was made to prevent a blocking of finalizer's thread + +## v2.4.7 - June 26, 2017 + * In JavaScriptEngineSwitcher.ChakraCore now the original exception is added to instance of the `JsRuntimeException` class as an inner exception + +## v2.4.6 - June 16, 2017 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.5.2 + +## v2.4.5 - June 8, 2017 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.5.1 + +## v2.4.4 - May 31, 2017 + * In JavaScriptEngineSwitcher.ChakraCore an attempt was made to prevent occurrence of the access violation exception + +## v2.4.3 - May 26, 2017 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.5.0 + +## v2.4.2 - May 24, 2017 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version of May 13, 2017 + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of May 24, 2017 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.4.4 + +## v2.4.1 - April 27, 2017 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.4.3 + +## v2.4.0 - April 26, 2017 + * Added support of .NET Core 1.0.4 + * In `IJsEngine` interface was added overloaded versions of the `Evaluate`, `Evaluate` and `Execute` methods, which take the document name as second parameter + * Now all JS engines provide extended information about the error location + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.2.1 + * In JavaScriptEngineSwitcher.V8: + * Added support of Microsoft ClearScript.V8 version of April 19, 2017 (support of V8 version 5.5.372.40) + * Now the `Evaluate` and `Execute` methods of `V8ScriptEngine` class are called with the `discard` parameter equal to `false` + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of March 9, 2017 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 2.10.4 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.4.2 + +## v2.3.2 - February 12, 2017 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.1.2 + * In JavaScriptEngineSwitcher.ChakraCore fixed a error causing a crash during finalization + +## v2.3.1 - February 10, 2017 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.1.1 + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of February 8, 2017 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.4.1 + +## v2.3.0 - January 16, 2017 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 2.10.3 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.4.0 + +## v2.2.0 - December 20, 2016 + * Added support of .NET Core 1.0.3 + * Downgraded .NET Framework version from 4.5.1 to 4.5 + * Now when you call the overloaded version of the `ExecuteResource` method, that takes the type, need to pass the resource name without the namespace + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.1.0 + * In JavaScriptEngineSwitcher.V8 now the Microsoft ClearScript.V8 requires `msvcp140.dll` assembly from the [Visual C++ Redistributable for Visual Studio 2015](https://www.microsoft.com/en-us/download/details.aspx?id=53840) + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of December 8, 2016 + * In JavaScriptEngineSwitcher.ChakraCore: + * Attempt to prevent occurrence of the access violation exception in the `CallFunction` method + * Fixed a error “Out of stack space” + +## v2.1.2 - November 8, 2016 + * Fixed a error #22 [“Make Exception serializable”](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/22) + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.0.1 + +## v2.1.1 - November 3, 2016 + * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.4.8 (support of V8 version 5.4.500.40) + * In JavaScriptEngineSwitcher.V8.Native.win-x86, JavaScriptEngineSwitcher.V8.Native.win-x64, JavaScriptEngineSwitcher.ChakraCore.Native.win-x86 and JavaScriptEngineSwitcher.ChakraCore.Native.win-x64 packages fixed a compatibility error with the .NET Framework 4.5.1 target in .NET Core Applications + +## v2.1.0 - November 2, 2016 + * In JavaScriptEngineSwitcher.V8: + * Fixed a error, that occurred during parsing of the original error message + * Native assemblies have been moved to separate packages: JavaScriptEngineSwitcher.V8.Native.win-x86 and JavaScriptEngineSwitcher.V8.Native.win-x64 + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of October 27, 2016 + * In JavaScriptEngineSwitcher.ChakraCore: + * Fixed a errors, that occurred during marshaling of Unicode strings in Unix-like operating systems + * Native assemblies for Windows have been moved to separate packages: JavaScriptEngineSwitcher.ChakraCore.Native.win-x86 and JavaScriptEngineSwitcher.ChakraCore.Native.win-x64 + * Added a packages, that contains a native assemblies for Windows (ARM), Debian-based Linux (x64) and OS X (x64) + * ChakraCore was updated to version of October 29, 2016 + * New version of the ChakraCore for Windows requires `msvcp140.dll` assembly from the [Visual C++ Redistributable for Visual Studio 2015](https://www.microsoft.com/en-us/download/details.aspx?id=53840) + +## v2.1.0 Beta 2 - October 31, 2016 + * In JavaScriptEngineSwitcher.ChakraCore added experimental support of Windows (ARM) + +## v2.1.0 Beta 1 - October 30, 2016 + * In JavaScriptEngineSwitcher.V8: + * Fixed a error, that occurred during parsing of the original error message + * Native assemblies have been moved to separate packages: JavaScriptEngineSwitcher.V8.Native.win-x86 and JavaScriptEngineSwitcher.V8.Native.win-x64 + * In JavaScriptEngineSwitcher.ChakraCore: + * Fixed a errors, that occurred during marshaling of Unicode strings in Unix-based operating systems + * Native assemblies have been moved to separate packages: JavaScriptEngineSwitcher.ChakraCore.Native.win-x86 and JavaScriptEngineSwitcher.ChakraCore.Native.win-x64 + * ChakraCore was updated to version of October 29, 2016 + * New version of the ChakraCore for Windows requires `msvcp140.dll` assembly from the [Visual C++ Redistributable for Visual Studio 2015](https://www.microsoft.com/en-us/download/details.aspx?id=53840) + * Added the JavaScriptEngineSwitcher.ChakraCore.Native.debian-x64 and JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64 packages + +## v2.0.3 - October 17, 2016 + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of October 17, 2016 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 2.10.2 + * In JavaScriptEngineSwitcher.ChakraCore was made switch to new ChakraCore API + +## v2.0.2 - October 16, 2016 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 2.10.1 and .NET Core 1.0.1 + +## v2.0.1 - October 6, 2016 + * Added module based on the [VroomJs](http://github.com/pauldotknopf/vroomjs-core). Special thanks to [Daniel Lo Nigro](http://github.com/Daniel15). + +## v2.0.0 - September 19, 2016 + * Removed dependency on `System.Configuration.dll` (no longer supported configuration by using the `Web.config` and `App.config` files) + * In JavaScriptEngineSwitcher.Core, JavaScriptEngineSwitcher.Msie (.NET Core version only works in JsRT modes) and JavaScriptEngineSwitcher.ChakraCore added support of .NET Core 1.0.1 + * Now all modules are support of .NET Framework 4.5.1 and can be used in web applications based on the “ASP.NET Core Web Application (.NET Framework)” template + * In `IJsEngine` interface was added `SupportsGarbageCollection` property and `CollectGarbage` method + * `JsRuntimeErrorHelpers` class was renamed to `JsErrorHelpers` class + * Created a JavaScriptEngineSwitcher.Extensions.MsDependencyInjection package, that contains extension methods for adding the JS engine switcher in an `IServiceCollection` + * JavaScriptEngineSwitcher.ConfigurationIntelliSense package is no longer required for the current version of the JavaScript Engine Switcher + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.0.0 + * In JavaScriptEngineSwitcher.V8: + * Added support of Microsoft ClearScript.V8 version 5.4.7 (support of V8 version 5.3.332.45) + * In configuration settings of the V8 JS engine was changed type of `DebugPort` property from `int` to `ushort` + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of September 19, 2016 + * In JavaScriptEngineSwitcher.ChakraCore: + * Added support of ChakraCore version 1.3 + * Added the ability to change configuration settings of the ChakraCore JS engine: `DisableBackgroundWork` (default `false`), `DisableNativeCodeGeneration` (default `false`), `DisableEval` (default `false`) and `EnableExperimentalFeatures` (default `false`) + +## v2.0.0 Beta 1 - September 17, 2016 + * In `IJsEngine` interface was added `SupportsGarbageCollection` property and `CollectGarbage` method + * `JsRuntimeErrorHelpers` class was renamed to `JsErrorHelpers` class + * Added support of .NET Core 1.0.1 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.0.0 Beta 2 + * In JavaScriptEngineSwitcher.V8: + * Added support of Microsoft ClearScript.V8 version 5.4.7 (support of V8 version 5.3.332.45) + * In configuration settings of the V8 JS engine was changed type of `DebugPort` property from `int` to `ushort` + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of September 13, 2016 + * In JavaScriptEngineSwitcher.ChakraCore: + * Added support of ChakraCore version 1.3 + * Added the ability to change configuration settings of the ChakraCore JS engine: `DisableBackgroundWork` (default `false`), `DisableNativeCodeGeneration` (default `false`), `DisableEval` (default `false`) and `EnableExperimentalFeatures` (default `false`) + +## v2.0.0 Alpha 2 - September 3, 2016 + * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 2.0.0 Alpha 1 + +## v2.0.0 Alpha 1 - August 23, 2016 + * Removed dependency on `System.Configuration.dll` (no longer supported configuration by using the `Web.config` and `App.config` files) + * In JavaScriptEngineSwitcher.Core and JavaScriptEngineSwitcher.ChakraCore added support of .NET Core 1.0 + * Now all modules are support of .NET Framework 4.5.1 and can be used in web applications based on the “ASP.NET Core Web Application (.NET Framework)” template + * Created a JavaScriptEngineSwitcher.Extensions.MsDependencyInjection package, that contains extension methods for adding the JS engine switcher in an `IServiceCollection` + * JavaScriptEngineSwitcher.ConfigurationIntelliSense package is no longer required for the current version of the JavaScript Engine Switcher + +## v1.5.9 - July 27, 2016 + * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of July 15, 2016 + * In JavaScriptEngineSwitcher.Jint added support of Jint version 2.9.1 + * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version 1.2 + +## v1.5.8 - June 30, 2016 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version of June 20, 2016 (support of V8 version 5.1.281.65) * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of June 29, 2016 -## June 16, 2016 - v1.5.7 +## v1.5.7 - June 16, 2016 * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of June 14, 2016 -## June 8, 2016 - v1.5.6 +## v1.5.6 - June 8, 2016 * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of June 4, 2016 -## May 30, 2016 - v1.5.5 +## v1.5.5 - May 30, 2016 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.4.6 (support of V8 version 5.1.281.50) -## May 24, 2016 - v1.5.4 +## v1.5.4 - May 24, 2016 * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.7.1 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version of May 9, 2016 * In JavaScriptEngineSwitcher.Jint added support of Jint version of May 12, 2016 * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version of May 24, 2016 -## April 13, 2016 - v1.5.3 +## v1.5.3 - April 13, 2016 * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of April 2, 2016 * In JavaScriptEngineSwitcher.Jint added support of Jint version of April 12, 2016 -## March 10, 2016 - v1.5.2 +## v1.5.2 - March 10, 2016 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.4.5 (support of V8 version 4.9.385.30) -## March 7, 2016 - v1.5.1 +## v1.5.1 - March 7, 2016 * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of March 5, 2016 * In JavaScriptEngineSwitcher.Jint added support of Jint version of March 6, 2016 * In JavaScriptEngineSwitcher.ChakraCore added support of ChakraCore version of March 6, 2016 -## March 4, 2016 - v1.5.0 +## v1.5.0 - March 4, 2016 * Added the `EmbedHostObject` (embeds a instance of simple class, structure or delegate to script code) and `EmbedHostType` (embeds a host type to script code) methods * In JavaScriptEngineSwitcher.ConfigurationIntelliSense updated definitions for configuration settings of MSIE JavaScript engine * Added module based on the ChakraCore @@ -41,183 +1106,183 @@ Change log * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of March 4, 2016 * In JavaScriptEngineSwitcher.Jint added support of Jint version of March 3, 2016 -## February 26, 2016 - v1.5.0 Beta 1 +## v1.5.0 Beta 1 - February 26, 2016 * Added the `EmbedHostType` method (embeds a host type to script code) * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.7.0 Beta 1 * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of February 24, 2016 * In JavaScriptEngineSwitcher.Jint added support of Jint version of February 18, 2016 -## February 5, 2016 - v1.5.0 Alpha 3 +## v1.5.0 Alpha 3 - February 5, 2016 * In JavaScriptEngineSwitcher.ChakraCore added support of the `EmbedHostObject` method -## January 16, 2016 - v1.5.0 Alpha 2 +## v1.5.0 Alpha 2 - January 16, 2016 * Added module based on the [ChakraCore](http://github.com/Microsoft/ChakraCore). JavaScriptEngineSwitcher.ChakraCore does not yet support the `EmbedHostObject` method. * In JavaScriptEngineSwitcher.ConfigurationIntelliSense updated definitions for configuration settings of MSIE JavaScript engine * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.7.0 Alpha 2 and in configuration settings was added one new property - `EnableDebugging` (default `false`) -## January 5, 2016 - v1.5.0 Alpha 1 +## v1.5.0 Alpha 1 - January 5, 2016 * Added the `EmbedHostObject` method (embeds a instance of simple class, structure or delegate to script code) -## December 8, 2015 - v1.4.1 +## v1.4.1 - December 8, 2015 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.4.4 (support of V8 version 4.7.80.25) * In JavaScriptEngineSwitcher.Jint added support of Jint version of December 2, 2015 -## December 3, 2015 - v1.4.0 +## v1.4.0 - December 3, 2015 * In JavaScriptEngineSwitcher.ConfigurationIntelliSense updated definitions for configuration settings of MSIE JavaScript engine * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.6.0 and `ChakraJsRt` mode was renamed to `ChakraIeJsRt` * In JavaScriptEngineSwitcher.V8 NuGet package solved the problem with restoring native assemblies -## November 5, 2015 - v1.3.1 +## v1.3.1 - November 5, 2015 * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of August 24, 2015 * In JavaScriptEngineSwitcher.Jint added support of Jint version 2.7.1 -## August 27, 2015 - v1.3.0 +## v1.3.0 - August 27, 2015 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.4.3 (support of V8 version 4.4.63.29) and now requires assemblies `msvcp120.dll` and `msvcr120.dll` from the [Visual C++ Redistributable Packages for Visual Studio 2013](http://www.microsoft.com/en-us/download/details.aspx?id=40784) -## July 23, 2015 - v1.2.11 +## v1.2.11 - July 23, 2015 * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.5.6 * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of July 14, 2015 * In JavaScriptEngineSwitcher.Jint added support of Jint version of July 8, 2015 -## June 29, 2015 - v1.2.10 +## v1.2.10 - June 29, 2015 * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.5.5 -## June 28, 2015 - v1.2.9 +## v1.2.9 - June 28, 2015 * In JavaScriptEngineSwitcher.ConfigurationIntelliSense updated definitions for configuration settings of MSIE and Jint JavaScript engines * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.5.4 and in configuration settings added 2 new properties: `UseEcmaScript5Polyfill` (default `false`) and `UseJson2Library` (default `false`) * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.4.2.1 (support of V8 version 4.2.77.18) * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of June 23, 2015 * In JavaScriptEngineSwitcher.Jint added support of Jint version of June 24, 2015 and in configuration settings added one new property - `AllowDebuggerStatement` (default `false`) -## May 26, 2015 - v1.2.8 +## v1.2.8 - May 26, 2015 * In JavaScriptEngineSwitcher.Jint added support of Jint version 2.5.0 -## May 11, 2015 - v1.2.7 +## v1.2.7 - May 11, 2015 * In JavaScriptEngineSwitcher.ConfigurationIntelliSense updated definitions for configuration settings of V8 JavaScript engine * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.4.2 (support of V8 version 4.2.77.18) -## May 5, 2015 - v1.2.6 +## v1.2.6 - May 5, 2015 * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.5.3 * In JavaScriptEngineSwitcher.V8 fixed a [bug #12](http://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/12) “V8 config can be null” * In JavaScriptEngineSwitcher.Jint added support of Jint version of April 30, 2015 -## April 5, 2015 - v1.2.5 +## v1.2.5 - April 5, 2015 * In JavaScriptEngineSwitcher.ConfigurationIntelliSense updated definitions for configuration settings of Jint JavaScript engine * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.5.2 * In JavaScriptEngineSwitcher.Jint added support of Jint version of March 29, 2015 and in configuration settings was changed default value of `MaxRecursionDepth` property from `20678` to `-1` -## February 19, 2015 - v1.2.4 +## v1.2.4 - February 19, 2015 * In `JsEngineBase` class all public non-abstract methods are now is virtual * In JavaScriptEngineSwitcher.ConfigurationIntelliSense added definitions for configuration settings of Jurassic and Jint JavaScript engines * In JavaScriptEngineSwitcher.Jurassic added the ability to change configuration settings of Jurassic JavaScript engine: `EnableDebugging` (default `false`), `EnableIlAnalysis` (default `false`) and `StrictMode` (default `false`) * In JavaScriptEngineSwitcher.Jint added the ability to change configuration settings of Jint JavaScript engine: `EnableDebugging` (default `false`), `MaxRecursionDepth` (default `20678`), `MaxStatements` (default `0`), `StrictMode` (default `false`) and `Timeout` (default `0`) -## February 16, 2015 - v1.2.3 +## v1.2.3 - February 16, 2015 * In JavaScriptEngineSwitcher.ConfigurationIntelliSense added definitions for configuration settings of V8 JavaScript engine * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.4.1 (support of V8 version 3.30.33.16) and added the ability to change configuration settings of V8 JavaScript engine: `EnableDebugging` (default `false`), `DebugPort` (default `9222`), `DisableGlobalMembers` (default `false`), `MaxNewSpaceSize` (default `0`), `MaxOldSpaceSize` (default `0`) and `MaxExecutableSize` (default `0`) * In JavaScriptEngineSwitcher.Jint added support of Jint version of February 14, 2015 -## January 13, 2015 - v1.2.2 +## v1.2.2 - January 13, 2015 * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.5.1 -## October 22, 2014 - v1.2.1 +## v1.2.1 - October 22, 2014 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version 5.4.0 (support of V8 version 3.26.31.15) * In JavaScriptEngineSwitcher.Jint added support of Jint version of October 21, 2014 -## October 13, 2014 - v1.2.0 +## v1.2.0 - October 13, 2014 * From JavaScriptEngineSwitcher.V8 and JavaScriptEngineSwitcher.Jint removed dependency on `System.Web.Extensions.dll` * All assemblies is now targeted on the .NET Framework 4 Client Profile * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.5.0 * In JavaScriptEngineSwitcher.Jint added support of Jint version of October 9, 2014 -## September 17, 2014 - v1.1.13 +## v1.1.13 - September 17, 2014 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version of September 6, 2014 * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of September 5, 2014 * In JavaScriptEngineSwitcher.Jint added support of Jint version of September 16, 2014 -## August 19, 2014 - v1.1.12 +## v1.1.12 - August 19, 2014 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version of August 1, 2014 * In JavaScriptEngineSwitcher.Jint added support of Jint version of August 16, 2014 -## July 22, 2014 - v1.1.11 +## v1.1.11 - July 22, 2014 * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.4.4 -## July 10, 2014 - v1.1.10 +## v1.1.10 - July 10, 2014 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version of July 5, 2014 * In JavaScriptEngineSwitcher.Jint added support of Jint version 2.2.0 -## June 14, 2014 - v1.1.9 +## v1.1.9 - June 14, 2014 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version of June 10, 2014 * In JavaScriptEngineSwitcher.Jint added support of Jint version of June 5, 2014 -## May 20, 2014 - v1.1.8 +## v1.1.8 - May 20, 2014 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version of May 17, 2014 * In JavaScriptEngineSwitcher.Jint added support of Jint version of May 16, 2014 -## May 7, 2014 - v1.1.7 +## v1.1.7 - May 7, 2014 * In JavaScriptEngineSwitcher.V8 added support of Microsoft ClearScript.V8 version of April 29, 2014 -## April 27, 2014 - v1.1.6 +## v1.1.6 - April 27, 2014 * In solution was enabled NuGet package restore * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.4.3 * In JavaScriptEngineSwitcher.Jurassic added support of Jurassic version of April 26, 2014 -## April 5, 2014 - v1.1.5 +## v1.1.5 - April 5, 2014 * In JavaScriptEngineSwitcher.Jint added support of Jint version of April 5, 2014 -## March 24, 2014 - v1.1.4 +## v1.1.4 - March 24, 2014 * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.4.2 -## March 22, 2014 - v1.1.3 +## v1.1.3 - March 22, 2014 * Added module based on the [Jint](http://github.com/sebastienros/jint). Special thanks to [Daniel Lo Nigro](http://github.com/Daniel15) for the idea of this module. * In JavaScriptEngineSwitcher.Core fixed minor bugs * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.4.1 -## February 27, 2014 - v1.1.2 +## v1.1.2 - February 27, 2014 * In JavaScriptEngineSwitcher.Msie added support of MSIE JavaScript Engine version 1.4.0 -## January 17, 2014 - v1.1.1 +## v1.1.1 - January 17, 2014 - * In JavaScriptEngineSwitcher.V8 added support of the [ClearScript](http://clearscript.codeplex.com/) version 5.3.11 (support of V8 version 3.24.17) + * In JavaScriptEngineSwitcher.V8 added support of the [ClearScript](http://github.com/Microsoft/ClearScript) version 5.3.11 (support of V8 version 3.24.17) -## January 16, 2014 - v1.1.0 +## v1.1.0 - January 16, 2014 * In JavaScriptEngineSwitcher.Msie added support of [MSIE JavaScript Engine](http://github.com/Taritsyn/MsieJavaScriptEngine) version 1.3.0 * In JavaScriptEngineSwitcher.V8 improved performance of `CallFunction` method - * In JavaScriptEngineSwitcher.Jurassic added support of [Jurassic](http://jurassic.codeplex.com/) version of January 11, 2014 + * In JavaScriptEngineSwitcher.Jurassic added support of [Jurassic](http://github.com/paulbartrum/jurassic) version of January 11, 2014 -## December 30, 2013 - v1.0.0 +## v1.0.0 - December 30, 2013 * Added support of JavaScript `undefined` type * In JavaScriptEngineSwitcher.Msie added support of [MSIE JavaScript Engine](http://github.com/Taritsyn/MsieJavaScriptEngine) version 1.2.0 -## December 7, 2013 - v0.9.5 +## v0.9.5 - December 7, 2013 - * In JavaScriptEngineSwitcher.V8 the [Noesis Javascript .NET](http://javascriptdotnet.codeplex.com/) was replaced by the [Microsoft ClearScript.V8](http://clearscript.codeplex.com/) library (solves a problem of `V8JsEngine` stable work on 64-bit version of IIS 8.X) - * In JavaScriptEngineSwitcher.Jurassic added support of [Jurassic](http://jurassic.codeplex.com/) version of September 30, 2013 - -## November 19, 2013 - v0.9.3 + * In JavaScriptEngineSwitcher.V8 the [Noesis Javascript .NET](http://github.com/JavascriptNet/Javascript.Net) was replaced by the [Microsoft ClearScript.V8](http://github.com/Microsoft/ClearScript) library (solves a problem of `V8JsEngine` stable work on 64-bit version of IIS 8.X) + * In JavaScriptEngineSwitcher.Jurassic added support of [Jurassic](http://github.com/paulbartrum/jurassic) version of September 30, 2013 + +## v0.9.3 - November 19, 2013 * In JavaScriptEngineSwitcher.V8 changed a mechanism for loading the Noesis Javascript .NET assemblies for different processor architectures * Deleted a `/configuration/jsEngineSwitcher/v8` configuration section -## September 5, 2013 - v0.9.2 +## v0.9.2 - September 5, 2013 * Added JavaScriptEngineSwitcher.ConfigurationIntelliSense NuGet package -## September 4, 2013 - v0.9.0 +## v0.9.0 - September 4, 2013 * Initial version uploaded \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..bbbdb4f8 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png index 8adeb31b..3d1a7d41 100644 Binary files a/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png and b/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png differ diff --git a/Icons/JavaScriptEngineSwitcher_ConfigurationIntelliSense_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_ConfigurationIntelliSense_Logo128x128.png index f6e382e4..6c8db785 100644 Binary files a/Icons/JavaScriptEngineSwitcher_ConfigurationIntelliSense_Logo128x128.png and b/Icons/JavaScriptEngineSwitcher_ConfigurationIntelliSense_Logo128x128.png differ diff --git a/Icons/JavaScriptEngineSwitcher_Core_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_Core_Logo128x128.png index b25bc40a..e7f7d970 100644 Binary files a/Icons/JavaScriptEngineSwitcher_Core_Logo128x128.png and b/Icons/JavaScriptEngineSwitcher_Core_Logo128x128.png differ diff --git a/Icons/JavaScriptEngineSwitcher_Extensions_MsDependencyInjection_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_Extensions_MsDependencyInjection_Logo128x128.png new file mode 100644 index 00000000..8a8587c4 Binary files /dev/null and b/Icons/JavaScriptEngineSwitcher_Extensions_MsDependencyInjection_Logo128x128.png differ diff --git a/Icons/JavaScriptEngineSwitcher_Jint_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_Jint_Logo128x128.png index 3357752f..b9ea16b3 100644 Binary files a/Icons/JavaScriptEngineSwitcher_Jint_Logo128x128.png and b/Icons/JavaScriptEngineSwitcher_Jint_Logo128x128.png differ diff --git a/Icons/JavaScriptEngineSwitcher_Jurassic_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_Jurassic_Logo128x128.png index cf631239..52a96ba6 100644 Binary files a/Icons/JavaScriptEngineSwitcher_Jurassic_Logo128x128.png and b/Icons/JavaScriptEngineSwitcher_Jurassic_Logo128x128.png differ diff --git a/Icons/JavaScriptEngineSwitcher_Msie_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_Msie_Logo128x128.png index 78d35ca1..070ee68b 100644 Binary files a/Icons/JavaScriptEngineSwitcher_Msie_Logo128x128.png and b/Icons/JavaScriptEngineSwitcher_Msie_Logo128x128.png differ diff --git a/Icons/JavaScriptEngineSwitcher_NiL_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_NiL_Logo128x128.png new file mode 100644 index 00000000..6b753f2a Binary files /dev/null and b/Icons/JavaScriptEngineSwitcher_NiL_Logo128x128.png differ diff --git a/Icons/JavaScriptEngineSwitcher_Node_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_Node_Logo128x128.png new file mode 100644 index 00000000..7ee2e17e Binary files /dev/null and b/Icons/JavaScriptEngineSwitcher_Node_Logo128x128.png differ diff --git a/Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png index 661fa22e..3cf8d976 100644 Binary files a/Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png and b/Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png differ diff --git a/Icons/JavaScriptEngineSwitcher_Vroom_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_Vroom_Logo128x128.png new file mode 100644 index 00000000..1730d00a Binary files /dev/null and b/Icons/JavaScriptEngineSwitcher_Vroom_Logo128x128.png differ diff --git a/Icons/JavaScriptEngineSwitcher_Yantra_Logo128x128.png b/Icons/JavaScriptEngineSwitcher_Yantra_Logo128x128.png new file mode 100644 index 00000000..578409d3 Binary files /dev/null and b/Icons/JavaScriptEngineSwitcher_Yantra_Logo128x128.png differ diff --git a/JavaScriptEngineSwitcher.NoSamples.slnx b/JavaScriptEngineSwitcher.NoSamples.slnx new file mode 100644 index 00000000..68112f6b --- /dev/null +++ b/JavaScriptEngineSwitcher.NoSamples.slnx @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JavaScriptEngineSwitcher.sln b/JavaScriptEngineSwitcher.sln deleted file mode 100644 index e1af278c..00000000 --- a/JavaScriptEngineSwitcher.sln +++ /dev/null @@ -1,76 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{3261AD87-FA7B-4B2B-9121-6E58F8B88B23}" - ProjectSection(SolutionItems) = preProject - .nuget\NuGet.Config = .nuget\NuGet.Config - .nuget\NuGet.exe = .nuget\NuGet.exe - .nuget\NuGet.targets = .nuget\NuGet.targets - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FF05A998-0DDD-40EC-9B92-7A8FF1AA9888}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0A72641D-5115-4199-9496-973092040ADF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaScriptEngineSwitcher.Core", "src\JavaScriptEngineSwitcher.Core\JavaScriptEngineSwitcher.Core.csproj", "{5C903EEF-BAD1-43B8-BFE2-E4EE4D204410}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaScriptEngineSwitcher.Jint", "src\JavaScriptEngineSwitcher.Jint\JavaScriptEngineSwitcher.Jint.csproj", "{26ECE52E-991F-4E12-BB88-467201038EFF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaScriptEngineSwitcher.Jurassic", "src\JavaScriptEngineSwitcher.Jurassic\JavaScriptEngineSwitcher.Jurassic.csproj", "{2E667689-F072-401F-A9A5-09F1A2ED025C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaScriptEngineSwitcher.Msie", "src\JavaScriptEngineSwitcher.Msie\JavaScriptEngineSwitcher.Msie.csproj", "{50AD3B1C-A295-42AC-979A-CD244429983C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaScriptEngineSwitcher.V8", "src\JavaScriptEngineSwitcher.V8\JavaScriptEngineSwitcher.V8.csproj", "{1BAEC601-B244-48D3-BE27-351E133EEF73}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaScriptEngineSwitcher.Tests", "test\JavaScriptEngineSwitcher.Tests\JavaScriptEngineSwitcher.Tests.csproj", "{7C91107D-6DC4-41FC-B976-0C76E1DFD52A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaScriptEngineSwitcher.ChakraCore", "src\JavaScriptEngineSwitcher.ChakraCore\JavaScriptEngineSwitcher.ChakraCore.csproj", "{698A1AFF-B84D-4FB1-B514-D18FFAB5066D}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5C903EEF-BAD1-43B8-BFE2-E4EE4D204410}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5C903EEF-BAD1-43B8-BFE2-E4EE4D204410}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5C903EEF-BAD1-43B8-BFE2-E4EE4D204410}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5C903EEF-BAD1-43B8-BFE2-E4EE4D204410}.Release|Any CPU.Build.0 = Release|Any CPU - {26ECE52E-991F-4E12-BB88-467201038EFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {26ECE52E-991F-4E12-BB88-467201038EFF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {26ECE52E-991F-4E12-BB88-467201038EFF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {26ECE52E-991F-4E12-BB88-467201038EFF}.Release|Any CPU.Build.0 = Release|Any CPU - {2E667689-F072-401F-A9A5-09F1A2ED025C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2E667689-F072-401F-A9A5-09F1A2ED025C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2E667689-F072-401F-A9A5-09F1A2ED025C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2E667689-F072-401F-A9A5-09F1A2ED025C}.Release|Any CPU.Build.0 = Release|Any CPU - {50AD3B1C-A295-42AC-979A-CD244429983C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50AD3B1C-A295-42AC-979A-CD244429983C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50AD3B1C-A295-42AC-979A-CD244429983C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50AD3B1C-A295-42AC-979A-CD244429983C}.Release|Any CPU.Build.0 = Release|Any CPU - {1BAEC601-B244-48D3-BE27-351E133EEF73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1BAEC601-B244-48D3-BE27-351E133EEF73}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1BAEC601-B244-48D3-BE27-351E133EEF73}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1BAEC601-B244-48D3-BE27-351E133EEF73}.Release|Any CPU.Build.0 = Release|Any CPU - {7C91107D-6DC4-41FC-B976-0C76E1DFD52A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7C91107D-6DC4-41FC-B976-0C76E1DFD52A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7C91107D-6DC4-41FC-B976-0C76E1DFD52A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7C91107D-6DC4-41FC-B976-0C76E1DFD52A}.Release|Any CPU.Build.0 = Release|Any CPU - {698A1AFF-B84D-4FB1-B514-D18FFAB5066D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {698A1AFF-B84D-4FB1-B514-D18FFAB5066D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {698A1AFF-B84D-4FB1-B514-D18FFAB5066D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {698A1AFF-B84D-4FB1-B514-D18FFAB5066D}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {5C903EEF-BAD1-43B8-BFE2-E4EE4D204410} = {FF05A998-0DDD-40EC-9B92-7A8FF1AA9888} - {26ECE52E-991F-4E12-BB88-467201038EFF} = {FF05A998-0DDD-40EC-9B92-7A8FF1AA9888} - {2E667689-F072-401F-A9A5-09F1A2ED025C} = {FF05A998-0DDD-40EC-9B92-7A8FF1AA9888} - {50AD3B1C-A295-42AC-979A-CD244429983C} = {FF05A998-0DDD-40EC-9B92-7A8FF1AA9888} - {1BAEC601-B244-48D3-BE27-351E133EEF73} = {FF05A998-0DDD-40EC-9B92-7A8FF1AA9888} - {698A1AFF-B84D-4FB1-B514-D18FFAB5066D} = {FF05A998-0DDD-40EC-9B92-7A8FF1AA9888} - {7C91107D-6DC4-41FC-B976-0C76E1DFD52A} = {0A72641D-5115-4199-9496-973092040ADF} - EndGlobalSection -EndGlobal diff --git a/JavaScriptEngineSwitcher.slnx b/JavaScriptEngineSwitcher.slnx new file mode 100644 index 00000000..839899a5 --- /dev/null +++ b/JavaScriptEngineSwitcher.slnx @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 8655371c..00000000 --- a/LICENSE +++ /dev/null @@ -1,169 +0,0 @@ -Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..a66f2b1f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,203 @@ + Copyright (c) 2013-2025 Andrey Taritsyn - http://www.taritsyn.ru + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Licenses/advanced-string-builder-license.txt b/Licenses/advanced-string-builder-license.txt new file mode 100644 index 00000000..3598e8b2 --- /dev/null +++ b/Licenses/advanced-string-builder-license.txt @@ -0,0 +1,203 @@ + Copyright (c) 2018-2024 Andrey Taritsyn - http://www.taritsyn.ru + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Licenses/chakra-core-license.txt b/Licenses/chakra-core-license.txt index 326104ed..f8ca6bbd 100644 --- a/Licenses/chakra-core-license.txt +++ b/Licenses/chakra-core-license.txt @@ -1,7 +1,8 @@ -ChakraCore The MIT License (MIT) Copyright (c) Microsoft Corporation +All rights reserved. +Copyright (c) ChakraCore Project Contributors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Licenses/clearscript-license.txt b/Licenses/clearscript-license.txt index f9d7c5bf..54b1b899 100644 --- a/Licenses/clearscript-license.txt +++ b/Licenses/clearscript-license.txt @@ -1,58 +1,22 @@ -Copyright (c) Microsoft Corporation. All rights reserved. - -Microsoft Public License (MS-PL) - -This license governs use of the accompanying software. If you use the -software, you accept this license. If you do not accept the license, do not -use the software. - -1. Definitions - - The terms "reproduce," "reproduction," "derivative works," and - "distribution" have the same meaning here as under U.S. copyright law. A - "contribution" is the original software, or any additions or changes to - the software. A "contributor" is any person that distributes its - contribution under this license. "Licensed patents" are a contributor's - patent claims that read directly on its contribution. - -2. Grant of Rights - - (A) Copyright Grant- Subject to the terms of this license, including the - license conditions and limitations in section 3, each contributor - grants you a non-exclusive, worldwide, royalty-free copyright license - to reproduce its contribution, prepare derivative works of its - contribution, and distribute its contribution or any derivative works - that you create. - - (B) Patent Grant- Subject to the terms of this license, including the - license conditions and limitations in section 3, each contributor - grants you a non-exclusive, worldwide, royalty-free license under its - licensed patents to make, have made, use, sell, offer for sale, - import, and/or otherwise dispose of its contribution in the software - or derivative works of the contribution in the software. - -3. Conditions and Limitations - - (A) No Trademark License- This license does not grant you rights to use - any contributors' name, logo, or trademarks. - - (B) If you bring a patent claim against any contributor over patents that - you claim are infringed by the software, your patent license from such - contributor to the software ends automatically. - - (C) If you distribute any portion of the software, you must retain all - copyright, patent, trademark, and attribution notices that are present - in the software. - - (D) If you distribute any portion of the software in source code form, you - may do so only under this license by including a complete copy of this - license with your distribution. If you distribute any portion of the - software in compiled or object code form, you may only do so under a - license that complies with this license. - - (E) The software is licensed "as-is." You bear the risk of using it. The - contributors give no express warranties, guarantees or conditions. You - may have additional consumer rights under your local laws which this - license cannot change. To the extent permitted under your local laws, - the contributors exclude the implied warranties of merchantability, - fitness for a particular purpose and non-infringement. \ No newline at end of file +MIT License + +Copyright (c) Microsoft Corporation +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Licenses/jering-javascript-nodejs-license.txt b/Licenses/jering-javascript-nodejs-license.txt new file mode 100644 index 00000000..fe0eb86a --- /dev/null +++ b/Licenses/jering-javascript-nodejs-license.txt @@ -0,0 +1,178 @@ +Copyright © 2018-2023 Jering. All rights reserved. + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/Binaries/ChakraCore/License.txt b/Licenses/microsoft-dotnet-arcade-sdk-license.txt similarity index 93% rename from Binaries/ChakraCore/License.txt rename to Licenses/microsoft-dotnet-arcade-sdk-license.txt index 326104ed..a616ed18 100644 --- a/Binaries/ChakraCore/License.txt +++ b/Licenses/microsoft-dotnet-arcade-sdk-license.txt @@ -1,7 +1,8 @@ -ChakraCore The MIT License (MIT) -Copyright (c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Licenses/msie-javascript-engine-license.txt b/Licenses/msie-javascript-engine-license.txt index 6cfac889..6e8f77b1 100644 --- a/Licenses/msie-javascript-engine-license.txt +++ b/Licenses/msie-javascript-engine-license.txt @@ -1,49 +1,203 @@ -Copyright (c) 2012-2016 Andrey Taritsyn - http://www.taritsyn.ru - -Microsoft Public License (Ms-PL) - -This license governs use of the accompanying software. If you use the software, -you accept this license. If you do not accept the license, do not use the -software. - -1. Definitions -The terms "reproduce," "reproduction," "derivative works," and "distribution" -have the same meaning here as under U.S. copyright law. A "contribution" is the -original software, or any additions or changes to the software. A "contributor" -is any person that distributes its contribution under this license. "Licensed -patents" are a contributor's patent claims that read directly on its -contribution. - -2. Grant of Rights -(A) Copyright Grant- Subject to the terms of this license, including the license -conditions and limitations in section 3, each contributor grants you a -non-exclusive, worldwide, royalty-free copyright license to reproduce its -contribution, prepare derivative works of its contribution, and distribute its -contribution or any derivative works that you create. -(B) Patent Grant- Subject -to the terms of this license, including the license conditions and limitations -in section 3, each contributor grants you a non-exclusive, worldwide, -royalty-free license under its licensed patents to make, have made, use, sell, -offer for sale, import, and/or otherwise dispose of its contribution in the -software or derivative works of the contribution in the software. - -3. Conditions and Limitations -(A) No Trademark License- This license does not grant you rights to use any -contributors' name, logo, or trademarks. -(B) If you bring a patent claim against any contributor over patents that you -claim are infringed by the software, your patent license from such contributor -to the software ends automatically. -(C) If you distribute any portion of the software, you must retain all -copyright, patent, trademark, and attribution notices that are present in the -software. -(D) If you distribute any portion of the software in source code form, you may -do so only under this license by including a complete copy of this license with -your distribution. If you distribute any portion of the software in compiled or -object code form, you may only do so under a license that complies with this -license. -(E) The software is licensed "as-is." You bear the risk of using it. The -contributors give no express warranties, guarantees or conditions. You may have -additional consumer rights under your local laws which this license cannot -change. To the extent permitted under your local laws, the contributors exclude -the implied warranties of merchantability, fitness for a particular purpose and -non-infringement. \ No newline at end of file + Copyright (c) 2012-2024 Andrey Taritsyn - http://www.taritsyn.ru + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Binaries/Jint/License.txt b/Licenses/nil-license.txt similarity index 84% rename from Binaries/Jint/License.txt rename to Licenses/nil-license.txt index b9fe813e..4f6ca166 100644 --- a/Binaries/Jint/License.txt +++ b/Licenses/nil-license.txt @@ -1,6 +1,4 @@ -BSD 2-Clause License - -Copyright (c) 2013, Sebastien Ros +Copyright (c) 2014-2021, NiLProject All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -9,4 +7,6 @@ Redistribution and use in source and binary forms, with or without modification, 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the "NiLProject" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Licenses/nodejs-license.txt b/Licenses/nodejs-license.txt new file mode 100644 index 00000000..41adca92 --- /dev/null +++ b/Licenses/nodejs-license.txt @@ -0,0 +1,2410 @@ +Node.js is licensed for use as follows: + +""" +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +This license applies to parts of Node.js originating from the +https://github.com/joyent/node repository: + +""" +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +The Node.js license applies to all parts of Node.js that are not externally +maintained libraries. + +The externally maintained libraries used by Node.js are: + +- Acorn, located at deps/acorn, is licensed as follows: + """ + MIT License + + Copyright (C) 2012-2022 by various contributors (see AUTHORS) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + """ + +- c-ares, located at deps/cares, is licensed as follows: + """ + MIT License + + Copyright (c) 1998 Massachusetts Institute of Technology + Copyright (c) 2007 - 2023 Daniel Stenberg with many contributors, see AUTHORS + file. + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + """ + +- cjs-module-lexer, located at deps/cjs-module-lexer, is licensed as follows: + """ + MIT License + ----------- + + Copyright (C) 2018-2020 Guy Bedford + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- ittapi, located at deps/v8/third_party/ittapi, is licensed as follows: + """ + Copyright (c) 2019 Intel Corporation. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ + +- ICU, located at deps/icu-small, is licensed as follows: + """ + UNICODE LICENSE V3 + + COPYRIGHT AND PERMISSION NOTICE + + Copyright © 2016-2023 Unicode, Inc. + + NOTICE TO USER: Carefully read the following legal agreement. BY + DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR + SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE + TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT + DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of data files and any associated documentation (the "Data Files") or + software and any associated documentation (the "Software") to deal in the + Data Files or Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, and/or sell + copies of the Data Files or Software, and to permit persons to whom the + Data Files or Software are furnished to do so, provided that either (a) + this copyright and permission notice appear with all copies of the Data + Files or Software, or (b) this copyright and permission notice appear in + associated Documentation. + + THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + THIRD PARTY RIGHTS. + + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE + BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, + OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA + FILES OR SOFTWARE. + + Except as contained in this notice, the name of a copyright holder shall + not be used in advertising or otherwise to promote the sale, use or other + dealings in these Data Files or Software without prior written + authorization of the copyright holder. + + ---------------------------------------------------------------------- + + Third-Party Software Licenses + + This section contains third-party software notices and/or additional + terms for licensed third-party software components included within ICU + libraries. + + ---------------------------------------------------------------------- + + ICU License - ICU 1.8.1 to ICU 57.1 + + COPYRIGHT AND PERMISSION NOTICE + + Copyright (c) 1995-2016 International Business Machines Corporation and others + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, and/or sell copies of the Software, and to permit persons + to whom the Software is furnished to do so, provided that the above + copyright notice(s) and this permission notice appear in all copies of + the Software and that both the above copyright notice(s) and this + permission notice appear in supporting documentation. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY + SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER + RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + Except as contained in this notice, the name of a copyright holder + shall not be used in advertising or otherwise to promote the sale, use + or other dealings in this Software without prior written authorization + of the copyright holder. + + All trademarks and registered trademarks mentioned herein are the + property of their respective owners. + + ---------------------------------------------------------------------- + + Chinese/Japanese Word Break Dictionary Data (cjdict.txt) + + # The Google Chrome software developed by Google is licensed under + # the BSD license. Other software included in this distribution is + # provided under other licenses, as set forth below. + # + # The BSD License + # http://opensource.org/licenses/bsd-license.php + # Copyright (C) 2006-2008, Google Inc. + # + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # Redistributions in binary form must reproduce the above + # copyright notice, this list of conditions and the following + # disclaimer in the documentation and/or other materials provided with + # the distribution. + # Neither the name of Google Inc. nor the names of its + # contributors may be used to endorse or promote products derived from + # this software without specific prior written permission. + # + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # + # + # The word list in cjdict.txt are generated by combining three word lists + # listed below with further processing for compound word breaking. The + # frequency is generated with an iterative training against Google web + # corpora. + # + # * Libtabe (Chinese) + # - https://sourceforge.net/project/?group_id=1519 + # - Its license terms and conditions are shown below. + # + # * IPADIC (Japanese) + # - http://chasen.aist-nara.ac.jp/chasen/distribution.html + # - Its license terms and conditions are shown below. + # + # ---------COPYING.libtabe ---- BEGIN-------------------- + # + # /* + # * Copyright (c) 1999 TaBE Project. + # * Copyright (c) 1999 Pai-Hsiang Hsiao. + # * All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . Redistributions in binary form must reproduce the above copyright + # * notice, this list of conditions and the following disclaimer in + # * the documentation and/or other materials provided with the + # * distribution. + # * . Neither the name of the TaBE Project nor the names of its + # * contributors may be used to endorse or promote products derived + # * from this software without specific prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # * OF THE POSSIBILITY OF SUCH DAMAGE. + # */ + # + # /* + # * Copyright (c) 1999 Computer Systems and Communication Lab, + # * Institute of Information Science, Academia + # * Sinica. All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . Redistributions in binary form must reproduce the above copyright + # * notice, this list of conditions and the following disclaimer in + # * the documentation and/or other materials provided with the + # * distribution. + # * . Neither the name of the Computer Systems and Communication Lab + # * nor the names of its contributors may be used to endorse or + # * promote products derived from this software without specific + # * prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # * OF THE POSSIBILITY OF SUCH DAMAGE. + # */ + # + # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, + # University of Illinois + # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 + # + # ---------------COPYING.libtabe-----END-------------------------------- + # + # + # ---------------COPYING.ipadic-----BEGIN------------------------------- + # + # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science + # and Technology. All Rights Reserved. + # + # Use, reproduction, and distribution of this software is permitted. + # Any copy of this software, whether in its original form or modified, + # must include both the above copyright notice and the following + # paragraphs. + # + # Nara Institute of Science and Technology (NAIST), + # the copyright holders, disclaims all warranties with regard to this + # software, including all implied warranties of merchantability and + # fitness, in no event shall NAIST be liable for + # any special, indirect or consequential damages or any damages + # whatsoever resulting from loss of use, data or profits, whether in an + # action of contract, negligence or other tortuous action, arising out + # of or in connection with the use or performance of this software. + # + # A large portion of the dictionary entries + # originate from ICOT Free Software. The following conditions for ICOT + # Free Software applies to the current dictionary as well. + # + # Each User may also freely distribute the Program, whether in its + # original form or modified, to any third party or parties, PROVIDED + # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear + # on, or be attached to, the Program, which is distributed substantially + # in the same form as set out herein and that such intended + # distribution, if actually made, will neither violate or otherwise + # contravene any of the laws and regulations of the countries having + # jurisdiction over the User or the intended distribution itself. + # + # NO WARRANTY + # + # The program was produced on an experimental basis in the course of the + # research and development conducted during the project and is provided + # to users as so produced on an experimental basis. Accordingly, the + # program is provided without any warranty whatsoever, whether express, + # implied, statutory or otherwise. The term "warranty" used herein + # includes, but is not limited to, any warranty of the quality, + # performance, merchantability and fitness for a particular purpose of + # the program and the nonexistence of any infringement or violation of + # any right of any third party. + # + # Each user of the program will agree and understand, and be deemed to + # have agreed and understood, that there is no warranty whatsoever for + # the program and, accordingly, the entire risk arising from or + # otherwise connected with the program is assumed by the user. + # + # Therefore, neither ICOT, the copyright holder, or any other + # organization that participated in or was otherwise related to the + # development of the program and their respective officials, directors, + # officers and other employees shall be held liable for any and all + # damages, including, without limitation, general, special, incidental + # and consequential damages, arising out of or otherwise in connection + # with the use or inability to use the program or any product, material + # or result produced or otherwise obtained by using the program, + # regardless of whether they have been advised of, or otherwise had + # knowledge of, the possibility of such damages at any time during the + # project or thereafter. Each user will be deemed to have agreed to the + # foregoing by his or her commencement of use of the program. The term + # "use" as used herein includes, but is not limited to, the use, + # modification, copying and distribution of the program and the + # production of secondary products from the program. + # + # In the case where the program, whether in its original form or + # modified, was distributed or delivered to or received by a user from + # any person, organization or entity other than ICOT, unless it makes or + # grants independently of ICOT any specific warranty to the user in + # writing, such person, organization or entity, will also be exempted + # from and not be held liable to the user for any such damages as noted + # above as far as the program is concerned. + # + # ---------------COPYING.ipadic-----END---------------------------------- + + ---------------------------------------------------------------------- + + Lao Word Break Dictionary Data (laodict.txt) + + # Copyright (C) 2016 and later: Unicode, Inc. and others. + # License & terms of use: http://www.unicode.org/copyright.html + # Copyright (c) 2015 International Business Machines Corporation + # and others. All Rights Reserved. + # + # Project: https://github.com/rober42539/lao-dictionary + # Dictionary: https://github.com/rober42539/lao-dictionary/laodict.txt + # License: https://github.com/rober42539/lao-dictionary/LICENSE.txt + # (copied below) + # + # This file is derived from the above dictionary version of Nov 22, 2020 + # ---------------------------------------------------------------------- + # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # Redistributions of source code must retain the above copyright notice, this + # list of conditions and the following disclaimer. Redistributions in binary + # form must reproduce the above copyright notice, this list of conditions and + # the following disclaimer in the documentation and/or other materials + # provided with the distribution. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # OF THE POSSIBILITY OF SUCH DAMAGE. + # -------------------------------------------------------------------------- + + ---------------------------------------------------------------------- + + Burmese Word Break Dictionary Data (burmesedict.txt) + + # Copyright (c) 2014 International Business Machines Corporation + # and others. All Rights Reserved. + # + # This list is part of a project hosted at: + # github.com/kanyawtech/myanmar-karen-word-lists + # + # -------------------------------------------------------------------------- + # Copyright (c) 2013, LeRoy Benjamin Sharon + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: Redistributions of source code must retain the above + # copyright notice, this list of conditions and the following + # disclaimer. Redistributions in binary form must reproduce the + # above copyright notice, this list of conditions and the following + # disclaimer in the documentation and/or other materials provided + # with the distribution. + # + # Neither the name Myanmar Karen Word Lists, nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + # SUCH DAMAGE. + # -------------------------------------------------------------------------- + + ---------------------------------------------------------------------- + + Time Zone Database + + ICU uses the public domain data and code derived from Time Zone + Database for its time zone support. The ownership of the TZ database + is explained in BCP 175: Procedure for Maintaining the Time Zone + Database section 7. + + # 7. Database Ownership + # + # The TZ database itself is not an IETF Contribution or an IETF + # document. Rather it is a pre-existing and regularly updated work + # that is in the public domain, and is intended to remain in the + # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do + # not apply to the TZ Database or contributions that individuals make + # to it. Should any claims be made and substantiated against the TZ + # Database, the organization that is providing the IANA + # Considerations defined in this RFC, under the memorandum of + # understanding with the IETF, currently ICANN, may act in accordance + # with all competent court orders. No ownership claims will be made + # by ICANN or the IETF Trust on the database or the code. Any person + # making a contribution to the database or code waives all rights to + # future claims in that contribution or in the TZ Database. + + ---------------------------------------------------------------------- + + Google double-conversion + + Copyright 2006-2011, the V8 project authors. All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------------- + + File: aclocal.m4 (only for ICU4C) + Section: pkg.m4 - Macros to locate and utilise pkg-config. + + Copyright © 2004 Scott James Remnant . + Copyright © 2012-2015 Dan Nicholson + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + + As a special exception to the GNU General Public License, if you + distribute this file as part of a program that contains a + configuration script generated by Autoconf, you may include it under + the same distribution terms that you use for the rest of that + program. + + (The condition for the exception is fulfilled because + ICU4C includes a configuration script generated by Autoconf, + namely the `configure` script.) + + ---------------------------------------------------------------------- + + File: config.guess (only for ICU4C) + + This file is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + + As a special exception to the GNU General Public License, if you + distribute this file as part of a program that contains a + configuration script generated by Autoconf, you may include it under + the same distribution terms that you use for the rest of that + program. This Exception is an additional permission under section 7 + of the GNU General Public License, version 3 ("GPLv3"). + + (The condition for the exception is fulfilled because + ICU4C includes a configuration script generated by Autoconf, + namely the `configure` script.) + + ---------------------------------------------------------------------- + + File: install-sh (only for ICU4C) + + Copyright 1991 by the Massachusetts Institute of Technology + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation, and that the name of M.I.T. not be used in advertising or + publicity pertaining to distribution of the software without specific, + written prior permission. M.I.T. makes no representations about the + suitability of this software for any purpose. It is provided "as is" + without express or implied warranty. + """ + +- libuv, located at deps/uv, is licensed as follows: + """ + Copyright (c) 2015-present libuv project contributors. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + This license applies to parts of libuv originating from the + https://github.com/joyent/libuv repository: + + ==== + + Copyright Joyent, Inc. and other Node contributors. All rights reserved. + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + + ==== + + This license applies to all parts of libuv that are not externally + maintained libraries. + + The externally maintained libraries used by libuv are: + + - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license. + + - inet_pton and inet_ntop implementations, contained in src/inet.c, are + copyright the Internet Systems Consortium, Inc., and licensed under the ISC + license. + """ + +- llhttp, located at deps/llhttp, is licensed as follows: + """ + This software is licensed under the MIT License. + + Copyright Fedor Indutny, 2018. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- corepack, located at deps/corepack, is licensed as follows: + """ + **Copyright © Corepack contributors** + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- undici, located at deps/undici, is licensed as follows: + """ + MIT License + + Copyright (c) Matteo Collina and Undici contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + """ + +- postject, located at test/fixtures/postject-copy, is licensed as follows: + """ + Postject is licensed for use as follows: + + """ + MIT License + + Copyright (c) 2022 Postman, Inc + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + """ + + The Postject license applies to all parts of Postject that are not externally + maintained libraries. + + The externally maintained libraries used by Postject are: + + - LIEF, located at vendor/LIEF, is licensed as follows: + """ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 - 2022 R. Thomas + Copyright 2017 - 2022 Quarkslab + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + """ + """ + +- OpenSSL, located at deps/openssl, is licensed as follows: + """ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + """ + +- Punycode.js, located at lib/punycode.js, is licensed as follows: + """ + Copyright Mathias Bynens + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- V8, located at deps/v8, is licensed as follows: + """ + This license applies to all parts of V8 that are not externally + maintained libraries. The externally maintained libraries used by V8 + are: + + - PCRE test suite, located in + test/mjsunit/third_party/regexp-pcre/regexp-pcre.js. This is based on the + test suite from PCRE-7.3, which is copyrighted by the University + of Cambridge and Google, Inc. The copyright notice and license + are embedded in regexp-pcre.js. + + - Layout tests, located in test/mjsunit/third_party/object-keys. These are + based on layout tests from webkit.org which are copyrighted by + Apple Computer, Inc. and released under a 3-clause BSD license. + + - Strongtalk assembler, the basis of the files assembler-arm-inl.h, + assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h, + assembler-ia32.cc, assembler-ia32.h, assembler-x64-inl.h, + assembler-x64.cc, assembler-x64.h, assembler.cc and assembler.h. + This code is copyrighted by Sun Microsystems Inc. and released + under a 3-clause BSD license. + + - Valgrind client API header, located at src/third_party/valgrind/valgrind.h + This is released under the BSD license. + + - The Wasm C/C++ API headers, located at third_party/wasm-api/wasm.{h,hh} + This is released under the Apache license. The API's upstream prototype + implementation also formed the basis of V8's implementation in + src/wasm/c-api.cc. + + These libraries have their own licenses; we recommend you read them, + as their terms may differ from the terms below. + + Further license information can be found in LICENSE files located in + sub-directories. + + Copyright 2014, the V8 project authors. All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ + +- SipHash, located at deps/v8/src/third_party/siphash, is licensed as follows: + """ + SipHash reference C implementation + + Copyright (c) 2016 Jean-Philippe Aumasson + + To the extent possible under law, the author(s) have dedicated all + copyright and related and neighboring rights to this software to the public + domain worldwide. This software is distributed without any warranty. + """ + +- zlib, located at deps/zlib, is licensed as follows: + """ + zlib.h -- interface of the 'zlib' general purpose compression library + version 1.3.0.1, August xxth, 2023 + + Copyright (C) 1995-2023 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + """ + +- simdjson, located at deps/simdjson, is licensed as follows: + """ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2023 The simdjson authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + """ + +- simdutf, located at deps/simdutf, is licensed as follows: + """ + Copyright 2021 The simdutf authors + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- ada, located at deps/ada, is licensed as follows: + """ + Copyright 2023 Yagiz Nizipli and Daniel Lemire + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- minimatch, located at deps/minimatch, is licensed as follows: + """ + The ISC License + + Copyright (c) 2011-2023 Isaac Z. Schlueter and Contributors + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + """ + +- npm, located at deps/npm, is licensed as follows: + """ + The npm application + Copyright (c) npm, Inc. and Contributors + Licensed on the terms of The Artistic License 2.0 + + Node package dependencies of the npm application + Copyright (c) their respective copyright owners + Licensed on their respective license terms + + The npm public registry at https://registry.npmjs.org + and the npm website at https://www.npmjs.com + Operated by npm, Inc. + Use governed by terms published on https://www.npmjs.com + + "Node.js" + Trademark Joyent, Inc., https://joyent.com + Neither npm nor npm, Inc. are affiliated with Joyent, Inc. + + The Node.js application + Project of Node Foundation, https://nodejs.org + + The npm Logo + Copyright (c) Mathias Pettersson and Brian Hammond + + "Gubblebum Blocky" typeface + Copyright (c) Tjarda Koster, https://jelloween.deviantart.com + Used with permission + + -------- + + The Artistic License 2.0 + + Copyright (c) 2000-2006, The Perl Foundation. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + This license establishes the terms under which a given free software + Package may be copied, modified, distributed, and/or redistributed. + The intent is that the Copyright Holder maintains some artistic + control over the development of that Package while still keeping the + Package available as open source and free software. + + You are always permitted to make arrangements wholly outside of this + license directly with the Copyright Holder of a given Package. If the + terms of this license do not permit the full use that you propose to + make of the Package, you should contact the Copyright Holder and seek + a different licensing arrangement. + + Definitions + + "Copyright Holder" means the individual(s) or organization(s) + named in the copyright notice for the entire Package. + + "Contributor" means any party that has contributed code or other + material to the Package, in accordance with the Copyright Holder's + procedures. + + "You" and "your" means any person who would like to copy, + distribute, or modify the Package. + + "Package" means the collection of files distributed by the + Copyright Holder, and derivatives of that collection and/or of + those files. A given Package may consist of either the Standard + Version, or a Modified Version. + + "Distribute" means providing a copy of the Package or making it + accessible to anyone else, or in the case of a company or + organization, to others outside of your company or organization. + + "Distributor Fee" means any fee that you charge for Distributing + this Package or providing support for this Package to another + party. It does not mean licensing fees. + + "Standard Version" refers to the Package if it has not been + modified, or has been modified only in ways explicitly requested + by the Copyright Holder. + + "Modified Version" means the Package, if it has been changed, and + such changes were not explicitly requested by the Copyright + Holder. + + "Original License" means this Artistic License as Distributed with + the Standard Version of the Package, in its current version or as + it may be modified by The Perl Foundation in the future. + + "Source" form means the source code, documentation source, and + configuration files for the Package. + + "Compiled" form means the compiled bytecode, object code, binary, + or any other form resulting from mechanical transformation or + translation of the Source form. + + Permission for Use and Modification Without Distribution + + (1) You are permitted to use the Standard Version and create and use + Modified Versions for any purpose without restriction, provided that + you do not Distribute the Modified Version. + + Permissions for Redistribution of the Standard Version + + (2) You may Distribute verbatim copies of the Source form of the + Standard Version of this Package in any medium without restriction, + either gratis or for a Distributor Fee, provided that you duplicate + all of the original copyright notices and associated disclaimers. At + your discretion, such verbatim copies may or may not include a + Compiled form of the Package. + + (3) You may apply any bug fixes, portability changes, and other + modifications made available from the Copyright Holder. The resulting + Package will still be considered the Standard Version, and as such + will be subject to the Original License. + + Distribution of Modified Versions of the Package as Source + + (4) You may Distribute your Modified Version as Source (either gratis + or for a Distributor Fee, and with or without a Compiled form of the + Modified Version) provided that you clearly document how it differs + from the Standard Version, including, but not limited to, documenting + any non-standard features, executables, or modules, and provided that + you do at least ONE of the following: + + (a) make the Modified Version available to the Copyright Holder + of the Standard Version, under the Original License, so that the + Copyright Holder may include your modifications in the Standard + Version. + + (b) ensure that installation of your Modified Version does not + prevent the user installing or running the Standard Version. In + addition, the Modified Version must bear a name that is different + from the name of the Standard Version. + + (c) allow anyone who receives a copy of the Modified Version to + make the Source form of the Modified Version available to others + under + + (i) the Original License or + + (ii) a license that permits the licensee to freely copy, + modify and redistribute the Modified Version using the same + licensing terms that apply to the copy that the licensee + received, and requires that the Source form of the Modified + Version, and of any works derived from it, be made freely + available in that license fees are prohibited but Distributor + Fees are allowed. + + Distribution of Compiled Forms of the Standard Version + or Modified Versions without the Source + + (5) You may Distribute Compiled forms of the Standard Version without + the Source, provided that you include complete instructions on how to + get the Source of the Standard Version. Such instructions must be + valid at the time of your distribution. If these instructions, at any + time while you are carrying out such distribution, become invalid, you + must provide new instructions on demand or cease further distribution. + If you provide valid instructions or cease distribution within thirty + days after you become aware that the instructions are invalid, then + you do not forfeit any of your rights under this license. + + (6) You may Distribute a Modified Version in Compiled form without + the Source, provided that you comply with Section 4 with respect to + the Source of the Modified Version. + + Aggregating or Linking the Package + + (7) You may aggregate the Package (either the Standard Version or + Modified Version) with other packages and Distribute the resulting + aggregation provided that you do not charge a licensing fee for the + Package. Distributor Fees are permitted, and licensing fees for other + components in the aggregation are permitted. The terms of this license + apply to the use and Distribution of the Standard or Modified Versions + as included in the aggregation. + + (8) You are permitted to link Modified and Standard Versions with + other works, to embed the Package in a larger work of your own, or to + build stand-alone binary or bytecode versions of applications that + include the Package, and Distribute the result without restriction, + provided the result does not expose a direct interface to the Package. + + Items That are Not Considered Part of a Modified Version + + (9) Works (including, but not limited to, modules and scripts) that + merely extend or make use of the Package, do not, by themselves, cause + the Package to be a Modified Version. In addition, such works are not + considered parts of the Package itself, and are not subject to the + terms of this license. + + General Provisions + + (10) Any use, modification, and distribution of the Standard or + Modified Versions is governed by this Artistic License. By using, + modifying or distributing the Package, you accept this license. Do not + use, modify, or distribute the Package, if you do not accept this + license. + + (11) If your Modified Version has been derived from a Modified + Version made by someone other than you, you are nevertheless required + to ensure that your Modified Version complies with the requirements of + this license. + + (12) This license does not grant you the right to use any trademark, + service mark, tradename, or logo of the Copyright Holder. + + (13) This license includes the non-exclusive, worldwide, + free-of-charge patent license to make, have made, use, offer to sell, + sell, import and otherwise transfer the Package with respect to any + patent claims licensable by the Copyright Holder that are necessarily + infringed by the Package. If you institute patent litigation + (including a cross-claim or counterclaim) against any party alleging + that the Package constitutes direct or contributory patent + infringement, then this Artistic License to you shall terminate on the + date that such litigation is filed. + + (14) Disclaimer of Warranty: + THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS + IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL + LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL + DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + -------- + """ + +- GYP, located at tools/gyp, is licensed as follows: + """ + Copyright (c) 2020 Node.js contributors. All rights reserved. + Copyright (c) 2009 Google Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ + +- inspector_protocol, located at tools/inspector_protocol, is licensed as follows: + """ + // Copyright 2016 The Chromium Authors. All rights reserved. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions are + // met: + // + // * Redistributions of source code must retain the above copyright + // notice, this list of conditions and the following disclaimer. + // * Redistributions in binary form must reproduce the above + // copyright notice, this list of conditions and the following disclaimer + // in the documentation and/or other materials provided with the + // distribution. + // * Neither the name of Google Inc. nor the names of its + // contributors may be used to endorse or promote products derived from + // this software without specific prior written permission. + // + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ + +- jinja2, located at tools/inspector_protocol/jinja2, is licensed as follows: + """ + Copyright (c) 2009 by the Jinja Team, see AUTHORS for more details. + + Some rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ + +- markupsafe, located at tools/inspector_protocol/markupsafe, is licensed as follows: + """ + Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS + for more details. + + Some rights reserved. + + Redistribution and use in source and binary forms of the software as well + as documentation, with or without modification, are permitted provided + that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + """ + +- cpplint.py, located at tools/cpplint.py, is licensed as follows: + """ + Copyright (c) 2009 Google Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ + +- gypi_to_gn.py, located at tools/gypi_to_gn.py, is licensed as follows: + """ + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ + +- ESLint, located at tools/node_modules/eslint, is licensed as follows: + """ + Copyright OpenJS Foundation and other contributors, + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + """ + +- gtest, located at deps/googletest, is licensed as follows: + """ + Copyright 2008, Google Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ + +- nghttp2, located at deps/nghttp2, is licensed as follows: + """ + The MIT License + + Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa + Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- large_pages, located at src/large_pages, is licensed as follows: + """ + Copyright (C) 2018 Intel Corporation + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- caja, located at lib/internal/freeze_intrinsics.js, is licensed as follows: + """ + Adapted from SES/Caja - Copyright (C) 2011 Google Inc. + Copyright (C) 2018 Agoric + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + """ + +- brotli, located at deps/brotli, is licensed as follows: + """ + Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + """ + +- HdrHistogram, located at deps/histogram, is licensed as follows: + """ + The code in this repository code was Written by Gil Tene, Michael Barker, + and Matt Warren, and released to the public domain, as explained at + http://creativecommons.org/publicdomain/zero/1.0/ + + For users of this code who wish to consume it under the "BSD" license + rather than under the public domain or CC0 contribution text mentioned + above, the code found under this directory is *also* provided under the + following license (commonly referred to as the BSD 2-Clause License). This + license does not detract from the above stated release of the code into + the public domain, and simply represents an additional license granted by + the Author. + + ----------------------------------------------------------------------------- + ** Beginning of "BSD 2-Clause License" text. ** + + Copyright (c) 2012, 2013, 2014 Gil Tene + Copyright (c) 2014 Michael Barker + Copyright (c) 2014 Matt Warren + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + """ + +- highlight.js, located at doc/api_assets/highlight.pack.js, is licensed as follows: + """ + BSD 3-Clause License + + Copyright (c) 2006, Ivan Sagalaev. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ + +- node-heapdump, located at src/heap_utils.cc, is licensed as follows: + """ + ISC License + + Copyright (c) 2012, Ben Noordhuis + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + === src/compat.h src/compat-inl.h === + + ISC License + + Copyright (c) 2014, StrongLoop Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + """ + +- rimraf, located at lib/internal/fs/rimraf.js, is licensed as follows: + """ + The ISC License + + Copyright (c) Isaac Z. Schlueter and Contributors + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + """ + +- uvwasi, located at deps/uvwasi, is licensed as follows: + """ + MIT License + + Copyright (c) 2019 Colin Ihrig and Contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + """ + +- ngtcp2, located at deps/ngtcp2/ngtcp2/, is licensed as follows: + """ + The MIT License + + Copyright (c) 2016 ngtcp2 contributors + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- nghttp3, located at deps/ngtcp2/nghttp3/, is licensed as follows: + """ + The MIT License + + Copyright (c) 2019 nghttp3 contributors + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- node-fs-extra, located at lib/internal/fs/cp, is licensed as follows: + """ + (The MIT License) + + Copyright (c) 2011-2017 JP Richardson + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files + (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- base64, located at deps/base64/base64/, is licensed as follows: + """ + Copyright (c) 2005-2007, Nick Galbreath + Copyright (c) 2015-2018, Wojciech Muła + Copyright (c) 2016-2017, Matthieu Darbois + Copyright (c) 2013-2022, Alfred Klomp + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ \ No newline at end of file diff --git a/Licenses/polyfills-for-old-dot-net-license.txt b/Licenses/polyfills-for-old-dot-net-license.txt new file mode 100644 index 00000000..3598e8b2 --- /dev/null +++ b/Licenses/polyfills-for-old-dot-net-license.txt @@ -0,0 +1,203 @@ + Copyright (c) 2018-2024 Andrey Taritsyn - http://www.taritsyn.ru + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Licenses/v8-license.txt b/Licenses/v8-license.txt index bf2314e3..24d1cd10 100644 --- a/Licenses/v8-license.txt +++ b/Licenses/v8-license.txt @@ -1,4 +1,4 @@ -Copyright 2006-2011, the V8 project authors. All rights reserved. +Copyright 2014, the V8 project authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Binaries/Jurassic/License.txt b/Licenses/vroomjs-core-license.txt similarity index 97% rename from Binaries/Jurassic/License.txt rename to Licenses/vroomjs-core-license.txt index 9fcc73ea..88b09b5a 100644 --- a/Binaries/Jurassic/License.txt +++ b/Licenses/vroomjs-core-license.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Paul Bartrum +Copyright (c) 2016 Paul Knopf Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Licenses/yantra-license.txt b/Licenses/yantra-license.txt new file mode 100644 index 00000000..f49a4e16 --- /dev/null +++ b/Licenses/yantra-license.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.ChakraCore/JavaScriptEngineSwitcher.ChakraCore.nuspec b/NuGet/JavaScriptEngineSwitcher.ChakraCore/JavaScriptEngineSwitcher.ChakraCore.nuspec deleted file mode 100644 index 370e7eb0..00000000 --- a/NuGet/JavaScriptEngineSwitcher.ChakraCore/JavaScriptEngineSwitcher.ChakraCore.nuspec +++ /dev/null @@ -1,43 +0,0 @@ - - - - JavaScriptEngineSwitcher.ChakraCore - 1.5.4 - JavaScript Engine Switcher for .Net: ChakraCore - Andrey Taritsyn - Andrey Taritsyn - http://github.com/Taritsyn/JavaScriptEngineSwitcher/blob/master/LICENSE - http://github.com/Taritsyn/JavaScriptEngineSwitcher - https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png - false - JavaScriptEngineSwitcher.ChakraCore contains adapter `ChakraCoreJsEngine` (wrapper for the ChakraCore (http://github.com/Microsoft/ChakraCore) version of May 24, 2016). Project was based on the code of Chakra-Samples (http://github.com/Microsoft/Chakra-Samples) and jsrt-dotnet (http://github.com/robpaveza/jsrt-dotnet). - -For correct working of the ChakraCore require assemblies `msvcp120.dll` and `msvcr120.dll` from the Visual C++ Redistributable Packages for Visual Studio 2013. - JavaScriptEngineSwitcher.ChakraCore contains adapter `ChakraCoreJsEngine` (wrapper for the ChakraCore version of May 24, 2016). - ChakraCore was updated to version of May 24, 2016. - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - en-US - JavaScript ECMAScript ChakraCore - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.ChakraCore/build-package.cmd b/NuGet/JavaScriptEngineSwitcher.ChakraCore/build-package.cmd deleted file mode 100644 index cfb2871c..00000000 --- a/NuGet/JavaScriptEngineSwitcher.ChakraCore/build-package.cmd +++ /dev/null @@ -1,2 +0,0 @@ -\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe ..\..\src\JavaScriptEngineSwitcher.ChakraCore\JavaScriptEngineSwitcher.ChakraCore.csproj /p:Configuration=Release -..\..\.nuget\nuget.exe pack ..\JavaScriptEngineSwitcher.ChakraCore\JavaScriptEngineSwitcher.ChakraCore.nuspec \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.ChakraCore/build/JavaScriptEngineSwitcher.ChakraCore.props b/NuGet/JavaScriptEngineSwitcher.ChakraCore/build/JavaScriptEngineSwitcher.ChakraCore.props deleted file mode 100644 index c689a97c..00000000 --- a/NuGet/JavaScriptEngineSwitcher.ChakraCore/build/JavaScriptEngineSwitcher.ChakraCore.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - ChakraCore\%(RecursiveDir)%(FileName)%(Extension) - PreserveNewest - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.ChakraCore/content/app.config.transform b/NuGet/JavaScriptEngineSwitcher.ChakraCore/content/app.config.transform deleted file mode 100644 index 0bcbaac7..00000000 --- a/NuGet/JavaScriptEngineSwitcher.ChakraCore/content/app.config.transform +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.ChakraCore/content/web.config.transform b/NuGet/JavaScriptEngineSwitcher.ChakraCore/content/web.config.transform deleted file mode 100644 index 717f0395..00000000 --- a/NuGet/JavaScriptEngineSwitcher.ChakraCore/content/web.config.transform +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.ChakraCore/readme.txt b/NuGet/JavaScriptEngineSwitcher.ChakraCore/readme.txt deleted file mode 100644 index 1232c93e..00000000 --- a/NuGet/JavaScriptEngineSwitcher.ChakraCore/readme.txt +++ /dev/null @@ -1,42 +0,0 @@ - - - ----------------------------------------------------------------------- - README file for JS Engine Switcher for .Net: ChakraCore 1.5.4 - - ----------------------------------------------------------------------- - - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - - - =========== - DESCRIPTION - =========== - JavaScriptEngineSwitcher.ChakraCore contains adapter - `ChakraCoreJsEngine` (wrapper for the ChakraCore - (http://github.com/Microsoft/ChakraCore) version of May 24, 2016). - Project was based on the code of Chakra-Samples - (http://github.com/Microsoft/Chakra-Samples) and jsrt-dotnet - (http://github.com/robpaveza/jsrt-dotnet). - - For correct working of the ChakraCore require assemblies `msvcp120.dll` - and `msvcr120.dll` from the Visual C++ Redistributable Packages for - Visual Studio 2013. - - ============= - RELEASE NOTES - ============= - ChakraCore was updated to version of May 24, 2016. - - ==================== - POST-INSTALL ACTIONS - ==================== - If in your system does not assemblies `msvcp120.dll` and - `msvcr120.dll`, then download and install the Visual C++ - Redistributable Packages for Visual Studio 2013 - (http://www.microsoft.com/en-us/download/details.aspx?id=40784). - - ============= - DOCUMENTATION - ============= - See documentation on GitHub - - http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.ChakraCore/tools/Install.ps1 b/NuGet/JavaScriptEngineSwitcher.ChakraCore/tools/Install.ps1 deleted file mode 100644 index 9cd5982f..00000000 --- a/NuGet/JavaScriptEngineSwitcher.ChakraCore/tools/Install.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -$assemblyDirectoryName = "ChakraCore" - -if ($project.Type -eq "Web Site") { - $projectDirectoryPath = $project.Properties.Item("FullPath").Value - $binDirectoryPath = Join-Path $projectDirectoryPath "bin" - $assemblyDirectoryPath = Join-Path $projectDirectoryPath $assemblyDirectoryName - - if (Test-Path $assemblyDirectoryPath) { - if (!(Test-Path $binDirectoryPath)) { - New-Item -ItemType Directory -Force -Path $binDirectoryPath - } - - Move-Item $assemblyDirectoryPath $binDirectoryPath -Force - } -} -else { - $assemblyDirectoryItem = $project.ProjectItems.Item($assemblyDirectoryName) - $assemblyDirectoryItem.Delete() -} \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.ChakraCore/tools/Uninstall.ps1 b/NuGet/JavaScriptEngineSwitcher.ChakraCore/tools/Uninstall.ps1 deleted file mode 100644 index a26fbb58..00000000 --- a/NuGet/JavaScriptEngineSwitcher.ChakraCore/tools/Uninstall.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -$assemblyDirectoryName = "ChakraCore" - -if ($project.Type -eq "Web Site") { - $projectDirectoryPath = $project.Properties.Item("FullPath").Value - $binDirectoryPath = Join-Path $projectDirectoryPath "bin" - $assemblyDirectoryPath = Join-Path $binDirectoryPath $assemblyDirectoryName - - if (Test-Path $assemblyDirectoryPath) { - Remove-Item $assemblyDirectoryPath -Force -Recurse - } -} \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.ConfigurationIntelliSense/JavaScriptEngineSwitcher.ConfigurationIntelliSense.nuspec b/NuGet/JavaScriptEngineSwitcher.ConfigurationIntelliSense/JavaScriptEngineSwitcher.ConfigurationIntelliSense.nuspec deleted file mode 100644 index 6f850a51..00000000 --- a/NuGet/JavaScriptEngineSwitcher.ConfigurationIntelliSense/JavaScriptEngineSwitcher.ConfigurationIntelliSense.nuspec +++ /dev/null @@ -1,23 +0,0 @@ - - - - JavaScriptEngineSwitcher.ConfigurationIntelliSense - 1.5.0 - JavaScript Engine Switcher for .Net: IntelliSense - Andrey Taritsyn - Andrey Taritsyn - http://github.com/Taritsyn/JavaScriptEngineSwitcher/blob/master/LICENSE - http://github.com/Taritsyn/JavaScriptEngineSwitcher - https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_ConfigurationIntelliSense_Logo128x128.png - false - JavaScriptEngineSwitcher.ConfigurationIntelliSense adds IntelliSense support when editing the `jsEngineSwitcher` configuration section in the Web.config or App.config file. - JavaScriptEngineSwitcher.ConfigurationIntelliSense adds IntelliSense support when editing the `jsEngineSwitcher` configuration section in the Web.config or App.config file. - Updated definitions for configuration settings of MSIE JavaScript engine. - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - en-US - App.config Web.config IntelliSense - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.ConfigurationIntelliSense/build-package.cmd b/NuGet/JavaScriptEngineSwitcher.ConfigurationIntelliSense/build-package.cmd deleted file mode 100644 index 7abbd682..00000000 --- a/NuGet/JavaScriptEngineSwitcher.ConfigurationIntelliSense/build-package.cmd +++ /dev/null @@ -1 +0,0 @@ -..\..\.nuget\nuget.exe pack ..\JavaScriptEngineSwitcher.ConfigurationIntelliSense\JavaScriptEngineSwitcher.ConfigurationIntelliSense.nuspec \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.ConfigurationIntelliSense/readme.txt b/NuGet/JavaScriptEngineSwitcher.ConfigurationIntelliSense/readme.txt deleted file mode 100644 index a818d311..00000000 --- a/NuGet/JavaScriptEngineSwitcher.ConfigurationIntelliSense/readme.txt +++ /dev/null @@ -1,27 +0,0 @@ - - - ------------------------------------------------------------------------- - README file for JS Engine Switcher for .Net: IntelliSense 1.5.0 - - ------------------------------------------------------------------------- - - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - - - =========== - DESCRIPTION - =========== - JavaScriptEngineSwitcher.ConfigurationIntelliSense adds IntelliSense - support when editing the `jsEngineSwitcher` configuration section in the - Web.config or App.config file. - - ============= - RELEASE NOTES - ============= - Updated definitions for configuration settings of MSIE JavaScript engine. - - ============= - DOCUMENTATION - ============= - See documentation on GitHub - - http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Core/JavaScriptEngineSwitcher.Core.nuspec b/NuGet/JavaScriptEngineSwitcher.Core/JavaScriptEngineSwitcher.Core.nuspec deleted file mode 100644 index 77324e49..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Core/JavaScriptEngineSwitcher.Core.nuspec +++ /dev/null @@ -1,31 +0,0 @@ - - - - JavaScriptEngineSwitcher.Core - 1.5.0 - JavaScript Engine Switcher for .Net: Core - Andrey Taritsyn - Andrey Taritsyn - http://github.com/Taritsyn/JavaScriptEngineSwitcher/blob/master/LICENSE - http://github.com/Taritsyn/JavaScriptEngineSwitcher - https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Core_Logo128x128.png - false - JavaScript Engine Switcher determines unified interface for access to the basic features of popular JavaScript engines (MSIE JavaScript Engine for .Net, Microsoft ClearScript.V8, Jurassic, Jint and ChakraCore). This library allows you to quickly and easily switch to using of another JavaScript engine. - JavaScript Engine Switcher determines unified interface for access to the basic features of popular JavaScript engines (MSIE JavaScript Engine for .Net, Microsoft ClearScript.V8, Jurassic, Jint and ChakraCore). - 1. Added the `EmbedHostObject` method (embeds a instance of simple class, structure or delegate to script code); -2. Added the `EmbedHostType` method (embeds a host type to script code). - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - en-US - JavaScript ECMAScript - - - - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Core/build-package.cmd b/NuGet/JavaScriptEngineSwitcher.Core/build-package.cmd deleted file mode 100644 index 71b135c5..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Core/build-package.cmd +++ /dev/null @@ -1,2 +0,0 @@ -\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe ..\..\src\JavaScriptEngineSwitcher.Core\JavaScriptEngineSwitcher.Core.csproj /p:Configuration=Release -..\..\.nuget\nuget.exe pack ..\JavaScriptEngineSwitcher.Core\JavaScriptEngineSwitcher.Core.nuspec \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Core/content/app.config.transform b/NuGet/JavaScriptEngineSwitcher.Core/content/app.config.transform deleted file mode 100644 index ec9f16c4..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Core/content/app.config.transform +++ /dev/null @@ -1,8 +0,0 @@ - - - -
- - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Core/content/web.config.transform b/NuGet/JavaScriptEngineSwitcher.Core/content/web.config.transform deleted file mode 100644 index ec9f16c4..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Core/content/web.config.transform +++ /dev/null @@ -1,8 +0,0 @@ - - - -
- - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Core/readme.txt b/NuGet/JavaScriptEngineSwitcher.Core/readme.txt deleted file mode 100644 index f83accbc..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Core/readme.txt +++ /dev/null @@ -1,31 +0,0 @@ - - - ---------------------------------------------------------------------- - README file for JS Engine Switcher for .Net: Core 1.5.0 - - ---------------------------------------------------------------------- - - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - - - =========== - DESCRIPTION - =========== - JavaScript Engine Switcher determines unified interface for access to - the basic features of popular JavaScript engines (MSIE JavaScript - Engine for .Net, Microsoft ClearScript.V8, Jurassic, Jint and - ChakraCore). This library allows you to quickly and easily switch to - using of another JavaScript engine. - - ============= - RELEASE NOTES - ============= - 1. Added the `EmbedHostObject` method (embeds a instance of simple class, - structure or delegate to script code); - 2. Added the `EmbedHostType` method (embeds a host type to script code). - - ============= - DOCUMENTATION - ============= - See documentation on GitHub - - http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Jint/JavaScriptEngineSwitcher.Jint.nuspec b/NuGet/JavaScriptEngineSwitcher.Jint/JavaScriptEngineSwitcher.Jint.nuspec deleted file mode 100644 index 27d6b779..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Jint/JavaScriptEngineSwitcher.Jint.nuspec +++ /dev/null @@ -1,36 +0,0 @@ - - - - JavaScriptEngineSwitcher.Jint - 1.5.4 - JavaScript Engine Switcher for .Net: Jint - Andrey Taritsyn - Andrey Taritsyn - http://github.com/Taritsyn/JavaScriptEngineSwitcher/blob/master/LICENSE - http://github.com/Taritsyn/JavaScriptEngineSwitcher - https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Jint_Logo128x128.png - false - JavaScriptEngineSwitcher.Jint contains adapter `JintJsEngine` (wrapper for the Jint JavaScript Engine (http://github.com/sebastienros/jint) version of May 12, 2016). - JavaScriptEngineSwitcher.Jint contains adapter `JintJsEngine` (wrapper for the Jint JavaScript Engine version of May 12, 2016). - Jint was updated to version of May 12, 2016. - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - en-US - JavaScript ECMAScript Jint - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Jint/build-package.cmd b/NuGet/JavaScriptEngineSwitcher.Jint/build-package.cmd deleted file mode 100644 index 3b3c0747..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Jint/build-package.cmd +++ /dev/null @@ -1,2 +0,0 @@ -\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe ..\..\src\JavaScriptEngineSwitcher.Jint\JavaScriptEngineSwitcher.Jint.csproj /p:Configuration=Release -..\..\.nuget\nuget.exe pack ..\JavaScriptEngineSwitcher.Jint\JavaScriptEngineSwitcher.Jint.nuspec \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Jint/content/app.config.transform b/NuGet/JavaScriptEngineSwitcher.Jint/content/app.config.transform deleted file mode 100644 index dcd70fbe..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Jint/content/app.config.transform +++ /dev/null @@ -1,14 +0,0 @@ - - - -
- - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Jint/content/web.config.transform b/NuGet/JavaScriptEngineSwitcher.Jint/content/web.config.transform deleted file mode 100644 index dcd70fbe..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Jint/content/web.config.transform +++ /dev/null @@ -1,14 +0,0 @@ - - - -
- - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Jint/readme.txt b/NuGet/JavaScriptEngineSwitcher.Jint/readme.txt deleted file mode 100644 index 22ae80e1..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Jint/readme.txt +++ /dev/null @@ -1,27 +0,0 @@ - - - ---------------------------------------------------------------------- - README file for JS Engine Switcher for .Net: Jint 1.5.4 - - ---------------------------------------------------------------------- - - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - - - =========== - DESCRIPTION - =========== - JavaScriptEngineSwitcher.Jint contains adapter `JintJsEngine` - (wrapper for the Jint JavaScript Engine - (http://github.com/sebastienros/jint) version of May 12, 2016). - - ============= - RELEASE NOTES - ============= - Jint was updated to version of May 12, 2016. - - ============= - DOCUMENTATION - ============= - See documentation on GitHub - - http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Jurassic/JavaScriptEngineSwitcher.Jurassic.nuspec b/NuGet/JavaScriptEngineSwitcher.Jurassic/JavaScriptEngineSwitcher.Jurassic.nuspec deleted file mode 100644 index cc504c80..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Jurassic/JavaScriptEngineSwitcher.Jurassic.nuspec +++ /dev/null @@ -1,36 +0,0 @@ - - - - JavaScriptEngineSwitcher.Jurassic - 1.5.8 - JavaScript Engine Switcher for .Net: Jurassic - Andrey Taritsyn - Andrey Taritsyn - http://github.com/Taritsyn/JavaScriptEngineSwitcher/blob/master/LICENSE - http://github.com/Taritsyn/JavaScriptEngineSwitcher - https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Jurassic_Logo128x128.png - false - JavaScriptEngineSwitcher.Jurassic contains adapter `JurassicJsEngine` (wrapper for the Jurassic JavaScript Engine (http://github.com/paulbartrum/jurassic) version of June 29, 2016). - JavaScriptEngineSwitcher.Jurassic contains adapter `JurassicJsEngine` (wrapper for the Jurassic JavaScript Engine version of June 29, 2016). - Jurassic was updated to version of June 29, 2016. - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - en-US - JavaScript ECMAScript Jurassic - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Jurassic/build-package.cmd b/NuGet/JavaScriptEngineSwitcher.Jurassic/build-package.cmd deleted file mode 100644 index 7e4e2e1f..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Jurassic/build-package.cmd +++ /dev/null @@ -1,2 +0,0 @@ -\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe ..\..\src\JavaScriptEngineSwitcher.Jurassic\JavaScriptEngineSwitcher.Jurassic.csproj /p:Configuration=Release -..\..\.nuget\nuget.exe pack ..\JavaScriptEngineSwitcher.Jurassic\JavaScriptEngineSwitcher.Jurassic.nuspec \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Jurassic/content/app.config.transform b/NuGet/JavaScriptEngineSwitcher.Jurassic/content/app.config.transform deleted file mode 100644 index d29967d1..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Jurassic/content/app.config.transform +++ /dev/null @@ -1,14 +0,0 @@ - - - -
- - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Jurassic/content/web.config.transform b/NuGet/JavaScriptEngineSwitcher.Jurassic/content/web.config.transform deleted file mode 100644 index d29967d1..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Jurassic/content/web.config.transform +++ /dev/null @@ -1,14 +0,0 @@ - - - -
- - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Jurassic/readme.txt b/NuGet/JavaScriptEngineSwitcher.Jurassic/readme.txt deleted file mode 100644 index bdc2fe0b..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Jurassic/readme.txt +++ /dev/null @@ -1,27 +0,0 @@ - - - ---------------------------------------------------------------------- - README file for JS Engine Switcher for .Net: Jurassic 1.5.8 - - ---------------------------------------------------------------------- - - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - - - =========== - DESCRIPTION - =========== - JavaScriptEngineSwitcher.Jurassic contains adapter `JurassicJsEngine` - (wrapper for the Jurassic JavaScript Engine - (http://github.com/paulbartrum/jurassic) version of June 29, 2016). - - ============= - RELEASE NOTES - ============= - Jurassic was updated to version of June 29, 2016. - - ============= - DOCUMENTATION - ============= - See documentation on GitHub - - http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Msie/JavaScriptEngineSwitcher.Msie.nuspec b/NuGet/JavaScriptEngineSwitcher.Msie/JavaScriptEngineSwitcher.Msie.nuspec deleted file mode 100644 index 66b0d950..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Msie/JavaScriptEngineSwitcher.Msie.nuspec +++ /dev/null @@ -1,35 +0,0 @@ - - - - JavaScriptEngineSwitcher.Msie - 1.5.4 - JavaScript Engine Switcher for .Net: MSIE - Andrey Taritsyn - Andrey Taritsyn - http://github.com/Taritsyn/JavaScriptEngineSwitcher/blob/master/LICENSE - http://github.com/Taritsyn/JavaScriptEngineSwitcher - https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Msie_Logo128x128.png - false - JavaScriptEngineSwitcher.Msie contains adapter `MsieJsEngine` (wrapper for the MSIE JavaScript Engine for .Net (http://github.com/Taritsyn/MsieJavaScriptEngine)). For correct working of the MSIE JavaScript Engine it is recommended to install Internet Explorer 9 and above on a server. - JavaScriptEngineSwitcher.Msie contains adapter `MsieJsEngine` (wrapper for the MSIE JavaScript Engine for .Net). - Added support of MSIE JavaScript Engine version 1.7.1. - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - en-US - JavaScript ECMAScript MSIE IE Chakra - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Msie/build-package.cmd b/NuGet/JavaScriptEngineSwitcher.Msie/build-package.cmd deleted file mode 100644 index cfccf447..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Msie/build-package.cmd +++ /dev/null @@ -1,2 +0,0 @@ -\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe ..\..\src\JavaScriptEngineSwitcher.Msie\JavaScriptEngineSwitcher.Msie.csproj /p:Configuration=Release -..\..\.nuget\nuget.exe pack ..\JavaScriptEngineSwitcher.Msie\JavaScriptEngineSwitcher.Msie.nuspec \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Msie/content/app.config.transform b/NuGet/JavaScriptEngineSwitcher.Msie/content/app.config.transform deleted file mode 100644 index b52bfb4b..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Msie/content/app.config.transform +++ /dev/null @@ -1,14 +0,0 @@ - - - -
- - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Msie/content/web.config.transform b/NuGet/JavaScriptEngineSwitcher.Msie/content/web.config.transform deleted file mode 100644 index b52bfb4b..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Msie/content/web.config.transform +++ /dev/null @@ -1,14 +0,0 @@ - - - -
- - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.Msie/readme.txt b/NuGet/JavaScriptEngineSwitcher.Msie/readme.txt deleted file mode 100644 index cd58e9ee..00000000 --- a/NuGet/JavaScriptEngineSwitcher.Msie/readme.txt +++ /dev/null @@ -1,29 +0,0 @@ - - - ---------------------------------------------------------------------- - README file for JS Engine Switcher for .Net: MSIE 1.5.4 - - ---------------------------------------------------------------------- - - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - - - =========== - DESCRIPTION - =========== - JavaScriptEngineSwitcher.Msie contains adapter `MsieJsEngine` (wrapper - for the MSIE JavaScript Engine for .Net - (http://github.com/Taritsyn/MsieJavaScriptEngine)). For correct - working of the MSIE JavaScript Engine it is recommended to install - Internet Explorer 9 and above on a server. - - ============= - RELEASE NOTES - ============= - Added support of MSIE JavaScript Engine version 1.7.1. - - ============= - DOCUMENTATION - ============= - See documentation on GitHub - - http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.V8/JavaScriptEngineSwitcher.V8.nuspec b/NuGet/JavaScriptEngineSwitcher.V8/JavaScriptEngineSwitcher.V8.nuspec deleted file mode 100644 index ac7cf2ce..00000000 --- a/NuGet/JavaScriptEngineSwitcher.V8/JavaScriptEngineSwitcher.V8.nuspec +++ /dev/null @@ -1,44 +0,0 @@ - - - - JavaScriptEngineSwitcher.V8 - 1.5.8 - JavaScript Engine Switcher for .Net: V8 - Andrey Taritsyn - Andrey Taritsyn - http://github.com/Taritsyn/JavaScriptEngineSwitcher/blob/master/LICENSE - http://github.com/Taritsyn/JavaScriptEngineSwitcher - https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png - false - JavaScriptEngineSwitcher.V8 contains adapter `V8JsEngine` (wrapper for the Microsoft ClearScript.V8 (http://clearscript.codeplex.com) version of June 20, 2016 with support of V8 version 5.1.281.65). For correct working of the Microsoft ClearScript.V8 require assemblies `msvcp120.dll` and `msvcr120.dll` from the Visual C++ Redistributable Packages for Visual Studio 2013. - JavaScriptEngineSwitcher.V8 contains adapter `V8JsEngine` (wrapper for the Microsoft ClearScript.V8 version of June 20, 2016 with support of V8 version 5.1.281.65). - Microsoft ClearScript.V8 was updated to version of June 20, 2016 (support of V8 version 5.1.281.65). - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - en-US - JavaScript ECMAScript V8 ClearScript - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.V8/build-package.cmd b/NuGet/JavaScriptEngineSwitcher.V8/build-package.cmd deleted file mode 100644 index d3c4ace3..00000000 --- a/NuGet/JavaScriptEngineSwitcher.V8/build-package.cmd +++ /dev/null @@ -1,2 +0,0 @@ -\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe ..\..\src\JavaScriptEngineSwitcher.V8\JavaScriptEngineSwitcher.V8.csproj /p:Configuration=Release -..\..\.nuget\nuget.exe pack ..\JavaScriptEngineSwitcher.V8\JavaScriptEngineSwitcher.V8.nuspec \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.V8/build/JavaScriptEngineSwitcher.V8.props b/NuGet/JavaScriptEngineSwitcher.V8/build/JavaScriptEngineSwitcher.V8.props deleted file mode 100644 index 0da088cf..00000000 --- a/NuGet/JavaScriptEngineSwitcher.V8/build/JavaScriptEngineSwitcher.V8.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - ClearScript.V8\%(FileName)%(Extension) - PreserveNewest - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.V8/content/app.config.transform b/NuGet/JavaScriptEngineSwitcher.V8/content/app.config.transform deleted file mode 100644 index b15e1384..00000000 --- a/NuGet/JavaScriptEngineSwitcher.V8/content/app.config.transform +++ /dev/null @@ -1,14 +0,0 @@ - - - -
- - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.V8/content/web.config.transform b/NuGet/JavaScriptEngineSwitcher.V8/content/web.config.transform deleted file mode 100644 index cf0b986e..00000000 --- a/NuGet/JavaScriptEngineSwitcher.V8/content/web.config.transform +++ /dev/null @@ -1,23 +0,0 @@ - - - -
- - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.V8/readme.txt b/NuGet/JavaScriptEngineSwitcher.V8/readme.txt deleted file mode 100644 index 6f913f48..00000000 --- a/NuGet/JavaScriptEngineSwitcher.V8/readme.txt +++ /dev/null @@ -1,39 +0,0 @@ - - - ----------------------------------------------------------------------- - README file for JS Engine Switcher for .Net: V8 1.5.8 - - ----------------------------------------------------------------------- - - Copyright (c) 2013-2016 Andrey Taritsyn - http://www.taritsyn.ru - - - =========== - DESCRIPTION - =========== - JavaScriptEngineSwitcher.V8 contains adapter `V8JsEngine` (wrapper for - the Microsoft ClearScript.V8 (http://clearscript.codeplex.com) version - of June 20, 2016 with support of V8 version 5.1.281.65. For correct - working of the Microsoft ClearScript.V8 require assemblies - `msvcp120.dll` and `msvcr120.dll` from the Visual C++ Redistributable - Packages for Visual Studio 2013. - - ============= - RELEASE NOTES - ============= - Microsoft ClearScript.V8 was updated to version of June 20, 2016 - (support of V8 version 5.1.281.65). - - ==================== - POST-INSTALL ACTIONS - ==================== - If in your system does not assemblies `msvcp120.dll` and - `msvcr120.dll`, then download and install the Visual C++ - Redistributable Packages for Visual Studio 2013 - (http://www.microsoft.com/en-us/download/details.aspx?id=40784). - - ============= - DOCUMENTATION - ============= - See documentation on GitHub - - http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.V8/tools/Install.ps1 b/NuGet/JavaScriptEngineSwitcher.V8/tools/Install.ps1 deleted file mode 100644 index ac764f04..00000000 --- a/NuGet/JavaScriptEngineSwitcher.V8/tools/Install.ps1 +++ /dev/null @@ -1,22 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -$assemblyDirectoryName = "ClearScript.V8" -$assemblyFileNames = "ClearScriptV8-32.dll", "v8-ia32.dll", "ClearScriptV8-64.dll", "v8-x64.dll" - -if ($project.Type -eq "Web Site") { - $projectDirectoryPath = $project.Properties.Item("FullPath").Value - $binDirectoryPath = Join-Path $projectDirectoryPath "bin" - $assemblyDirectoryPath = Join-Path $projectDirectoryPath $assemblyDirectoryName - - if (Test-Path $assemblyDirectoryPath) { - if (!(Test-Path $binDirectoryPath)) { - New-Item -ItemType Directory -Force -Path $binDirectoryPath - } - - Move-Item $assemblyDirectoryPath $binDirectoryPath -Force - } -} -else { - $assemblyDirectoryItem = $project.ProjectItems.Item($assemblyDirectoryName) - $assemblyDirectoryItem.Delete() -} \ No newline at end of file diff --git a/NuGet/JavaScriptEngineSwitcher.V8/tools/Uninstall.ps1 b/NuGet/JavaScriptEngineSwitcher.V8/tools/Uninstall.ps1 deleted file mode 100644 index 9532fbb6..00000000 --- a/NuGet/JavaScriptEngineSwitcher.V8/tools/Uninstall.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -$assemblyDirectoryName = "ClearScript.V8" - -if ($project.Type -eq "Web Site") { - $projectDirectoryPath = $project.Properties.Item("FullPath").Value - $binDirectoryPath = Join-Path $projectDirectoryPath "bin" - $assemblyDirectoryPath = Join-Path $binDirectoryPath $assemblyDirectoryName - - if (Test-Path $assemblyDirectoryPath) { - Remove-Item $assemblyDirectoryPath -Force -Recurse - } -} \ No newline at end of file diff --git a/NuGet/build-all-packages.cmd b/NuGet/build-all-packages.cmd deleted file mode 100644 index bdf535f5..00000000 --- a/NuGet/build-all-packages.cmd +++ /dev/null @@ -1,22 +0,0 @@ -set packages_directory="C:\Projects\JavaScriptEngineSwitcher\NuGet" - -cd %packages_directory%\JavaScriptEngineSwitcher.Core -call build-package.cmd - -cd %packages_directory%\JavaScriptEngineSwitcher.ConfigurationIntelliSense -call build-package.cmd - -cd %packages_directory%\JavaScriptEngineSwitcher.Jint -call build-package.cmd - -cd %packages_directory%\JavaScriptEngineSwitcher.Jurassic -call build-package.cmd - -cd %packages_directory%\JavaScriptEngineSwitcher.Msie -call build-package.cmd - -cd %packages_directory%\JavaScriptEngineSwitcher.V8 -call build-package.cmd - -cd %packages_directory%\JavaScriptEngineSwitcher.ChakraCore -call build-package.cmd \ No newline at end of file diff --git a/NuGet/move-packages-to-nuget-repository.cmd b/NuGet/move-packages-to-nuget-repository.cmd deleted file mode 100644 index 00f55be7..00000000 --- a/NuGet/move-packages-to-nuget-repository.cmd +++ /dev/null @@ -1,10 +0,0 @@ -set packages_directory="C:\Projects\JavaScriptEngineSwitcher\NuGet" -set repository_directory="C:\NuGet Repository" - -move %packages_directory%\JavaScriptEngineSwitcher.Core\*.nupkg %repository_directory% -move %packages_directory%\JavaScriptEngineSwitcher.ConfigurationIntelliSense\*.nupkg %repository_directory% -move %packages_directory%\JavaScriptEngineSwitcher.Jint\*.nupkg %repository_directory% -move %packages_directory%\JavaScriptEngineSwitcher.Jurassic\*.nupkg %repository_directory% -move %packages_directory%\JavaScriptEngineSwitcher.Msie\*.nupkg %repository_directory% -move %packages_directory%\JavaScriptEngineSwitcher.V8\*.nupkg %repository_directory% -move %packages_directory%\JavaScriptEngineSwitcher.ChakraCore\*.nupkg %repository_directory% \ No newline at end of file diff --git a/README.md b/README.md index 03b23a92..f94e0e9b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -JavaScript Engine Switcher +JavaScript Engine Switcher [![NuGet version](http://img.shields.io/nuget/v/JavaScriptEngineSwitcher.Core.svg)](https://www.nuget.org/packages/JavaScriptEngineSwitcher.Core/) [![Download count](https://img.shields.io/nuget/dt/JavaScriptEngineSwitcher.Core.svg)](https://www.nuget.org/packages/JavaScriptEngineSwitcher.Core/) ========================== -JavaScript Engine Switcher determines unified interface for access to the basic features of popular JavaScript engines ([MSIE JavaScript Engine for .Net](http://github.com/Taritsyn/MsieJavaScriptEngine), [Microsoft ClearScript.V8](http://clearscript.codeplex.com), [Jurassic](http://github.com/paulbartrum/jurassic), [Jint](http://github.com/sebastienros/jint) and [ChakraCore](http://github.com/Microsoft/ChakraCore)). +JavaScript Engine Switcher determines unified interface for access to the basic features of popular JavaScript engines ([ChakraCore](https://github.com/chakra-core/ChakraCore), [Jint](https://github.com/sebastienros/jint), [Jurassic](https://github.com/paulbartrum/jurassic), [MSIE JavaScript Engine for .NET](https://github.com/Taritsyn/MsieJavaScriptEngine), [NiL.JS](https://github.com/nilproject/NiL.JS), [Jering.Javascript.NodeJS](https://github.com/JeringTech/Javascript.NodeJS), [Microsoft ClearScript.V8](https://github.com/Microsoft/ClearScript), [VroomJs](https://github.com/pauldotknopf/vroomjs-core) and [YantraJS](https://github.com/yantrajs/yantra)). This library allows you to quickly and easily switch to using of another JavaScript engine. The supported .NET types are as follows: @@ -15,26 +15,63 @@ The supported .NET types are as follows: ## Installation This library can be installed through NuGet: - * [JS Engine Switcher: Core](http://nuget.org/packages/JavaScriptEngineSwitcher.Core) - * [JS Engine Switcher: IntelliSense](http://nuget.org/packages/JavaScriptEngineSwitcher.ConfigurationIntelliSense) - * [JS Engine Switcher: MSIE](http://nuget.org/packages/JavaScriptEngineSwitcher.Msie) - * [JS Engine Switcher: V8](http://nuget.org/packages/JavaScriptEngineSwitcher.V8) - * [JS Engine Switcher: Jurassic](http://nuget.org/packages/JavaScriptEngineSwitcher.Jurassic) - * [JS Engine Switcher: Jint](http://nuget.org/packages/JavaScriptEngineSwitcher.Jint) - * [JS Engine Switcher: ChakraCore](http://nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore) +### Core + * [JS Engine Switcher: Core](http://nuget.org/packages/JavaScriptEngineSwitcher.Core) (supports .NET Framework 4.0 Client, .NET Framework 4.5, .NET Standard 1.3 and .NET Standard 2.0) + * [JS Engine Switcher: MS Dependency Injection](http://nuget.org/packages/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection) (supports .NET Framework 4.5, .NET Standard 1.3 and .NET Standard 2.0) + +### JS engines + * [JS Engine Switcher: ChakraCore](http://nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore) (supports .NET Framework 4.0 Client, .NET Framework 4.5, .NET Framework 4.7.1, .NET Standard 1.3, .NET Standard 2.0 and .NET Standard 2.1) + * [Windows (x86)](http://nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86) + * [Windows (x64)](http://nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64) + * [Windows (ARM)](http://nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm) + * [Windows (ARM64)](http://nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64) + * [Linux (x64)](http://nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64) + * [OS X (x64)](http://nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64) + * [JS Engine Switcher: Jint](http://nuget.org/packages/JavaScriptEngineSwitcher.Jint) (supports .NET Framework 4.6.2, .NET Standard 2.0, .NET Standard 2.1 and .NET 8) + * [JS Engine Switcher: Jurassic](http://nuget.org/packages/JavaScriptEngineSwitcher.Jurassic) (supports .NET Framework 4.0 Client, .NET Framework 4.5 and .NET Standard 2.0) + * [JS Engine Switcher: MSIE](http://nuget.org/packages/JavaScriptEngineSwitcher.Msie) (supports .NET Framework 4.0 Client, .NET Framework 4.5, .NET Standard 1.3 and .NET Standard 2.0) + * [JS Engine Switcher: NiL](http://nuget.org/packages/JavaScriptEngineSwitcher.NiL) (supports .NET Framework 4.6.1, .NET Framework 4.8, .NET Core App 3.1, .NET 6, .NET 8 and .NET 9) + * [JS Engine Switcher: Node](http://nuget.org/packages/JavaScriptEngineSwitcher.Node) (supports .NET Framework 4.6.1, .NET Standard 2.0, .NET Core App 3.1, .NET 5.0, .NET 6 and .NET 7) + * [JS Engine Switcher: V8](http://nuget.org/packages/JavaScriptEngineSwitcher.V8) (supports .NET Framework 4.6.2, .NET Framework 4.7.1, .NET Standard 2.1, .NET Core App 3.1 and .NET 5.0) + * [Microsoft ClearScript.V8 for Windows (x86)](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.win-x86) + * [Microsoft ClearScript.V8 for Windows (x64)](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.win-x64) + * [Microsoft ClearScript.V8 for Windows (ARM64)](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.win-arm64) + * [Microsoft ClearScript.V8 for Linux (x64)](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.linux-x64) + * [Microsoft ClearScript.V8 for Linux (ARM)](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.linux-arm) + * [Microsoft ClearScript.V8 for Linux (ARM64)](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.linux-arm64) + * [Microsoft ClearScript.V8 for OS X (x64)](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.osx-x64) + * [Microsoft ClearScript.V8 for OS X (ARM64)](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.osx-arm64) + * [JS Engine Switcher: Vroom](http://nuget.org/packages/JavaScriptEngineSwitcher.Vroom) (supports .NET Framework 4.0 Client, .NET Framework 4.5, .NET Framework 4.7.1, .NET Standard 1.6 and .NET Standard 2.0) + * [JS Engine Switcher: Yantra](http://nuget.org/packages/JavaScriptEngineSwitcher.Yantra) (supports .NET Standard 2.0 and .NET Standard 2.1) + +If you have used the JavaScript Engine Switcher version 2.X, then I recommend to first read [“How to upgrade applications to version 3.X”](https://github.com/Taritsyn/JavaScriptEngineSwitcher/wiki/How-to-upgrade-applications-to-version-3.X) section of the documentation. + +## Documentation +Documentation is located on the [wiki](https://github.com/Taritsyn/JavaScriptEngineSwitcher/wiki) of this Repo. ## Release History See the [changelog](CHANGELOG.md). ## License -[Apache License Version 2.0](http://github.com/Taritsyn/JavaScriptEngineSwitcher/blob/master/LICENSE) +[Apache License Version 2.0](http://github.com/Taritsyn/JavaScriptEngineSwitcher/blob/master/LICENSE.txt) ## Who's Using JavaScript Engine Switcher If you use the JavaScript Engine Switcher in some project, please send me a message so I can include it in this list: - * [Bundle Transformer](http://bundletransformer.codeplex.com/) by Andrey Taritsyn - * [Cruncher](http://github.com/JimBobSquarePants/Cruncher) by James South + * [AbundantMusic.NET](https://github.com/Connor14/AbundantMusic.NET) by Connor Schmidt + * [Autoprefixer Host for .NET](https://github.com/Taritsyn/AutoprefixerHost) by Andrey Taritsyn + * [Bundle Transformer](https://github.com/Taritsyn/BundleTransformer) by Andrey Taritsyn + * [Cruncher](https://github.com/JimBobSquarePants/Cruncher) by James South + * [Dart Sass Host for .NET](https://github.com/Taritsyn/DartSassHost) by Andrey Taritsyn + * [E.F.F.C JavaScriptEngineSwitcher Extention Lib](https://github.com/redwolf0817/EFFC.JavaScriptEngineSwitcher.Extention) by ItTrending + * [GFMParserSample.Net](https://github.com/mad4-red/GFMParserSample.Net) * [JSPool](http://dan.cx/projects/jspool) by Daniel Lo Nigro + * [ProteanCMS](https://github.com/Eonic/ProteanCMS) by Trevor Spink + * [QSI](https://github.com/chequer-io/qsi) * [ReactJS.NET](http://reactjs.net/) by Daniel Lo Nigro - * [T1.Scripts](http://nuget.org/packages/T1.Scripts) - * [zxcvbn.net](http://github.com/darcythomas/zxcvbn.net) by Darcy Thomas \ No newline at end of file + * [SSR.Net](https://github.com/knowit/SSR.Net) + * [Statiq Framework](https://statiq.dev/framework) (formerly known as [Wyam](http://wyam.io/)) + * [T1.Scripts](http://nuget.org/packages/T1.Scripts) + * [VIEApps.Services.Base](https://github.com/vieapps/Services.Base) + * [YouTubeStreamsExtractor](https://github.com/tmk907/YouTubeStreamsExtractor) + * [zxcvbn.net](https://github.com/darcythomas/zxcvbn.net) by Darcy Thomas \ No newline at end of file diff --git a/JavaScriptEngineSwitcher.snk b/build/Key.snk similarity index 100% rename from JavaScriptEngineSwitcher.snk rename to build/Key.snk diff --git a/build/common.props b/build/common.props new file mode 100644 index 00000000..bd3a2ae5 --- /dev/null +++ b/build/common.props @@ -0,0 +1,7 @@ + + + Copyright © 2013-2025 Andrey Taritsyn + false + false + + \ No newline at end of file diff --git a/build/initialize-standard-nuspec-properties.targets b/build/initialize-standard-nuspec-properties.targets new file mode 100644 index 00000000..5cc44de4 --- /dev/null +++ b/build/initialize-standard-nuspec-properties.targets @@ -0,0 +1,142 @@ + + + + + + + + $(NuspecPackageId) + $(RepositoryUrl) + + + + + + + + + + <_TitleElement/> + <_TitleElement Condition="'$(Title)' != ''"> + $(Title) + + + <_ReadmeElement/> + <_ReadmeElement Condition="'$(PackageReadmeFile)' != ''"> + $(PackageReadmeFile) + + + <_ReadmeFileElement/> + <_ReadmeFileElement Condition="'$(PackageReadmeFile)' != ''"> + + + + <_LicenseElement/> + <_LicenseElement Condition="'$(PackageLicenseExpression)' != ''"> + $(PackageLicenseExpression) + + <_LicenseElement Condition="'$(PackageLicenseFile)' != ''"> + $(PackageLicenseFile) + + + <_LicenseFileElement/> + <_LicenseFileElement Condition="'$(PackageLicenseFullPath)' != '' and '$(PackageLicenseFile)' != ''"> + + + <_LicenseFileElement Condition="'$(PackageLicenseFullPath)' != '' and '$(PackageLicenseFile)' == ''"> + + + + <_TagsElement/> + <_TagsElement Condition="'$(PackageTags)' != ''"> + $(PackageTags.Replace(';', ' ')) + + + <_IconUrlElement/> + <_IconUrlElement Condition="'$(PackageIconUrl)' != ''"> + $(PackageIconUrl) + + + <_IconElement/> + <_IconElement Condition="'$(PackageIcon)' != ''"> + $(PackageIcon) + + + <_IconFileElement/> + <_IconFileElement Condition="'$(PackageIcon)' != ''"> + + + + <_ReleaseNotesElement/> + <_ReleaseNotesElement Condition="'$(PackageReleaseNotes)' != ''"> + $(PackageReleaseNotes) + + + <_DevelopmentDependencyElement/> + <_DevelopmentDependencyElement Condition="'$(DevelopmentDependency)' != ''"> + $(DevelopmentDependency) + + + <_ServiceableElement/> + <_ServiceableElement Condition="'$(Serviceable)' != ''"> + $(Serviceable) + + + <_NeutralLanguageElement/> + <_NeutralLanguageElement Condition="'$(NeutralLanguage)' != ''"> + $(NeutralLanguage) + + + <_CommonMetadataElements> + $(PackageId) + $(PackageVersion) + $(_TitleElement) + $(Authors) + $(_LicenseElement) + $(PackageRequireLicenseAcceptance) + $(_IconElement) + $(_ReadmeElement) + $(PackageProjectUrl) + $(_IconUrlElement) + $(PackageDescription) + $(_ReleaseNotesElement) + $(Copyright) + $(_TagsElement) + + $(_DevelopmentDependencyElement) + $(_ServiceableElement) + $(_NeutralLanguageElement) + + + <_CommonFileElements> + $(CommonFileElements) + $(_IconFileElement) + $(_LicenseFileElement) + $(_ReadmeFileElement) + + + + + + + + + + + + @(NuspecProperty, ';') + + + \ No newline at end of file diff --git a/build/net40-client-target.props b/build/net40-client-target.props new file mode 100644 index 00000000..a6ecf137 --- /dev/null +++ b/build/net40-client-target.props @@ -0,0 +1,7 @@ + + + .NETFramework + v4.0 + client + + \ No newline at end of file diff --git a/build/nuget-common.props b/build/nuget-common.props new file mode 100644 index 00000000..e36818c8 --- /dev/null +++ b/build/nuget-common.props @@ -0,0 +1,18 @@ + + + $(Product) + Andrey Taritsyn + PACKAGE-DESCRIPTION.md + $(PackageReadmeFile) + JavaScriptEngineSwitcher;JavaScript;ECMAScript + Apache-2.0 + ../../LICENSE.txt + icon.png + https://github.com/Taritsyn/JavaScriptEngineSwitcher + https://github.com/Taritsyn/JavaScriptEngineSwitcher + git + en-US + ../../nuget + true + + \ No newline at end of file diff --git a/build/nuget-for-dotnet-lib.props b/build/nuget-for-dotnet-lib.props new file mode 100644 index 00000000..45e6e499 --- /dev/null +++ b/build/nuget-for-dotnet-lib.props @@ -0,0 +1,21 @@ + + + + + true + true + snupkg + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/nuget-for-native-lib.props b/build/nuget-for-native-lib.props new file mode 100644 index 00000000..396aa563 --- /dev/null +++ b/build/nuget-for-native-lib.props @@ -0,0 +1,11 @@ + + + + + false + $(MSBuildProjectName).nuspec + + + + + \ No newline at end of file diff --git a/build/strong-name-signing.props b/build/strong-name-signing.props new file mode 100644 index 00000000..543e8a3a --- /dev/null +++ b/build/strong-name-signing.props @@ -0,0 +1,7 @@ + + + ../../build/Key.snk + true + true + + \ No newline at end of file diff --git a/global.json b/global.json new file mode 100644 index 00000000..512142d2 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "10.0.100", + "rollForward": "latestFeature" + } +} diff --git a/lib/ChakraCore/License.txt b/lib/ChakraCore/License.txt new file mode 100644 index 00000000..f8ca6bbd --- /dev/null +++ b/lib/ChakraCore/License.txt @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) Microsoft Corporation +All rights reserved. +Copyright (c) ChakraCore Project Contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/lib/ChakraCore/runtimes/linux-x64/native/libChakraCore.so b/lib/ChakraCore/runtimes/linux-x64/native/libChakraCore.so new file mode 100644 index 00000000..8bb7f46b Binary files /dev/null and b/lib/ChakraCore/runtimes/linux-x64/native/libChakraCore.so differ diff --git a/lib/ChakraCore/runtimes/osx-x64/native/libChakraCore.dylib b/lib/ChakraCore/runtimes/osx-x64/native/libChakraCore.dylib new file mode 100644 index 00000000..f0c98abf Binary files /dev/null and b/lib/ChakraCore/runtimes/osx-x64/native/libChakraCore.dylib differ diff --git a/lib/ChakraCore/runtimes/win-arm/native/ChakraCore.dll b/lib/ChakraCore/runtimes/win-arm/native/ChakraCore.dll new file mode 100644 index 00000000..9a940729 Binary files /dev/null and b/lib/ChakraCore/runtimes/win-arm/native/ChakraCore.dll differ diff --git a/lib/ChakraCore/runtimes/win-arm64/native/ChakraCore.dll b/lib/ChakraCore/runtimes/win-arm64/native/ChakraCore.dll new file mode 100644 index 00000000..4f16911b Binary files /dev/null and b/lib/ChakraCore/runtimes/win-arm64/native/ChakraCore.dll differ diff --git a/lib/ChakraCore/runtimes/win-x64/native/ChakraCore.dll b/lib/ChakraCore/runtimes/win-x64/native/ChakraCore.dll new file mode 100644 index 00000000..8711905f Binary files /dev/null and b/lib/ChakraCore/runtimes/win-x64/native/ChakraCore.dll differ diff --git a/lib/ChakraCore/runtimes/win-x86/native/ChakraCore.dll b/lib/ChakraCore/runtimes/win-x86/native/ChakraCore.dll new file mode 100644 index 00000000..fa05c533 Binary files /dev/null and b/lib/ChakraCore/runtimes/win-x86/native/ChakraCore.dll differ diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/.bowerrc b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/.bowerrc new file mode 100644 index 00000000..e5b93c99 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/.bowerrc @@ -0,0 +1,4 @@ +{ + "registry": "https://registry.bower.io", + "directory": "lib" +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/App_Start/FilterConfig.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/App_Start/FilterConfig.cs new file mode 100644 index 00000000..d652502c --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/App_Start/FilterConfig.cs @@ -0,0 +1,12 @@ +using System.Web.Mvc; + +namespace JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4 +{ + public class FilterConfig + { + public static void RegisterGlobalFilters(GlobalFilterCollection filters) + { + filters.Add(new HandleErrorAttribute()); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/App_Start/JsEngineSwitcherConfig.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/App_Start/JsEngineSwitcherConfig.cs new file mode 100644 index 00000000..354e6e2d --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/App_Start/JsEngineSwitcherConfig.cs @@ -0,0 +1,25 @@ +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.Vroom; + +namespace JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4 +{ + public class JsEngineSwitcherConfig + { + public static void Configure(IJsEngineSwitcher engineSwitcher) + { + engineSwitcher.EngineFactories + .AddChakraCore() + .AddJurassic() + .AddMsie(new MsieSettings + { + EngineMode = JsEngineMode.ChakraIeJsRt + }) + .AddVroom() + ; + engineSwitcher.DefaultEngineName = ChakraCoreJsEngine.EngineName; + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/App_Start/RouteConfig.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/App_Start/RouteConfig.cs new file mode 100644 index 00000000..285b32db --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/App_Start/RouteConfig.cs @@ -0,0 +1,19 @@ +using System.Web.Mvc; +using System.Web.Routing; + +namespace JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4 +{ + public class RouteConfig + { + public static void RegisterRoutes(RouteCollection routes) + { + routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + + routes.MapRoute( + name: "Default", + url: "{controller}/{action}/{id}", + defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } + ); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Controllers/HomeController.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Controllers/HomeController.cs new file mode 100644 index 00000000..9835159c --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Controllers/HomeController.cs @@ -0,0 +1,68 @@ +using System.Configuration; +using System.Web.Mvc; + +using JavaScriptEngineSwitcher.Sample.Logic.Services; + +namespace JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4.Controllers +{ + public class HomeController : Controller + { + private readonly FileContentService _fileContentService; + private readonly JsEvaluationService _jsEvaluationService; + + + public HomeController() + : this( + new FileContentService(ConfigurationManager.AppSettings["jsengineswitcher:Samples:TextContentDirectoryPath"]), + new JsEvaluationService() + ) + { } + + public HomeController(FileContentService fileContentService, JsEvaluationService jsEvaluationService) + { + _fileContentService = fileContentService; + _jsEvaluationService = jsEvaluationService; + } + + + public ActionResult Index() + { + ViewBag.Body = _fileContentService.GetFileContent("index.html"); + + return View(); + } + + [HttpGet] + public ActionResult Demo() + { + var model = _jsEvaluationService.GetInitializationData(); + + return View(model); + } + + [HttpPost] + [ValidateInput(false)] + public ActionResult Demo(FormCollection collection) + { + var model = _jsEvaluationService.GetInitializationData(); + + TryUpdateModel(model, new[] { "EngineName", "Expression" }, collection); + + if (ModelState.IsValid) + { + model = _jsEvaluationService.Evaluate(model); + + ModelState.Clear(); + } + + return View(model); + } + + public ActionResult Contact() + { + ViewBag.Body = _fileContentService.GetFileContent("contact.html"); + + return View(); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Global.asax b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Global.asax new file mode 100644 index 00000000..d503ecf6 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4.MvcApplication" Language="C#" %> \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Global.asax.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Global.asax.cs new file mode 100644 index 00000000..e0052650 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Global.asax.cs @@ -0,0 +1,20 @@ +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4 +{ + public class MvcApplication : HttpApplication + { + protected void Application_Start() + { + AreaRegistration.RegisterAllAreas(); + + FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); + RouteConfig.RegisterRoutes(RouteTable.Routes); + JsEngineSwitcherConfig.Configure(JsEngineSwitcher.Current); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Infrastructure/Helpers/CommonExtensions.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Infrastructure/Helpers/CommonExtensions.cs new file mode 100644 index 00000000..597799b5 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Infrastructure/Helpers/CommonExtensions.cs @@ -0,0 +1,33 @@ +using System.Globalization; +using System.Text.RegularExpressions; +using System.Web.Mvc; + +namespace JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4.Infrastructure.Helpers +{ + public static class CommonExtensions + { + private static readonly HelperConstants _constants = new HelperConstants(); + + public static HelperConstants Constants(this HtmlHelper htmlHelper) + { + return _constants; + } + + public static MvcHtmlString EncodedReplace(this HtmlHelper htmlHelper, string input, + string pattern, string replacement) + { + return new MvcHtmlString(Regex.Replace(htmlHelper.Encode(input), pattern, replacement)); + } + + internal static string CreateSubIndexName(this HtmlHelper htmlHelper, string prefix, int index) + { + string name = prefix; + if (index >= 0) + { + name = string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", prefix, index); + } + + return name; + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Infrastructure/Helpers/HelperConstants.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Infrastructure/Helpers/HelperConstants.cs new file mode 100644 index 00000000..b2ca7747 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Infrastructure/Helpers/HelperConstants.cs @@ -0,0 +1,7 @@ +namespace JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4.Infrastructure.Helpers +{ + public sealed class HelperConstants + { + public readonly string RequiredFieldMarkerCssClassName = "required-field-marker"; + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Infrastructure/Helpers/LabelExtensions.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Infrastructure/Helpers/LabelExtensions.cs new file mode 100644 index 00000000..7d51ed8b --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Infrastructure/Helpers/LabelExtensions.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Web.Mvc; +using System.Linq.Expressions; + +namespace JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4.Infrastructure.Helpers +{ + public static class LabelExtensions + { + public static MvcHtmlString CustomLabel(this HtmlHelper htmlHelper, string name, string labelText, + bool isRequired, bool withColon, object htmlAttributes) + { + return htmlHelper.CustomLabel(name, labelText, isRequired, withColon, + HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); + } + + public static MvcHtmlString CustomLabel(this HtmlHelper htmlHelper, string name, string labelText, + bool isRequired, bool withColon, IDictionary htmlAttributes) + { + string id = TagBuilder.CreateSanitizedId(name); + + var label = new TagBuilder("label"); + label.MergeAttribute("for", id); + if (htmlAttributes != null) + { + label.MergeAttributes(htmlAttributes); + } + label.InnerHtml = labelText + ((withColon) ? ":" : String.Empty); + if (isRequired) + { + var requiredFieldMarker = new TagBuilder("span"); + requiredFieldMarker.SetInnerText("*"); + requiredFieldMarker.AddCssClass(htmlHelper.Constants().RequiredFieldMarkerCssClassName); + + label.InnerHtml += requiredFieldMarker.ToString(); + } + + return MvcHtmlString.Create(label.ToString()); + } + + + public static MvcHtmlString CustomLabelFor(this HtmlHelper htmlHelper, + Expression> expression) + { + return htmlHelper.CustomLabelFor(expression, false, false, null); + } + + public static MvcHtmlString CustomLabelFor(this HtmlHelper htmlHelper, + Expression> expression, object htmlAttributes) + { + return htmlHelper.CustomLabelFor(expression, false, false, + HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); + } + + public static MvcHtmlString CustomLabelFor(this HtmlHelper htmlHelper, + Expression> expression, IDictionary htmlAttributes) + { + return htmlHelper.CustomLabelFor(expression, false, false, htmlAttributes); + } + + public static MvcHtmlString CustomLabelFor(this HtmlHelper htmlHelper, + Expression> expression, bool withColon) + { + return htmlHelper.CustomLabelFor(expression, withColon, false, null); + } + + public static MvcHtmlString CustomLabelFor(this HtmlHelper htmlHelper, + Expression> expression, bool withColon, object htmlAttributes) + { + return htmlHelper.CustomLabelFor(expression, withColon, false, + HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); + } + + public static MvcHtmlString CustomLabelFor(this HtmlHelper htmlHelper, + Expression> expression, bool withColon, IDictionary htmlAttributes) + { + return htmlHelper.CustomLabelFor(expression, withColon, false, htmlAttributes); + } + + public static MvcHtmlString CustomLabelFor(this HtmlHelper htmlHelper, + Expression> expression, bool withColon, bool hideRequiredFieldMarker) + { + return htmlHelper.CustomLabelFor(expression, withColon, hideRequiredFieldMarker, null); + } + + public static MvcHtmlString CustomLabelFor(this HtmlHelper htmlHelper, + Expression> expression, bool withColon, bool hideRequiredFieldMarker, + object htmlAttributes) + { + return htmlHelper.CustomLabelFor(expression, withColon, hideRequiredFieldMarker, + HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); + } + + public static MvcHtmlString CustomLabelFor(this HtmlHelper htmlHelper, + Expression> expression, bool withColon, bool hideRequiredFieldMarker, + IDictionary htmlAttributes) + { + ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); + string name = ExpressionHelper.GetExpressionText(expression); + string labelText = metadata.DisplayName; + bool isRequired = false; + if (!hideRequiredFieldMarker && !metadata.IsReadOnly && metadata.ShowForEdit) + { + foreach (ModelValidator validator in metadata.GetValidators(htmlHelper.ViewContext.Controller.ControllerContext)) + { + if (validator.IsRequired) + { + isRequired = true; + break; + } + } + } + + return htmlHelper.CustomLabel(name, labelText, isRequired, withColon, htmlAttributes); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4.csproj new file mode 100644 index 00000000..32393355 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4.csproj @@ -0,0 +1,219 @@ + + + + + + + + + Debug + AnyCPU + + + 2.0 + {8124DF18-4A0B-4648-8B90-4A6D3E4EF2F3} + {E3E379DF-F4C6-4180-9B81-6769533ABE47};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4 + JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4 + v4.0 + false + true + + + + + ..\..\ + true + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + + + ..\..\packages\Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.0\lib\net40\Microsoft.Web.Mvc.FixedDisplayModes.dll + + + + + + + ..\..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll + + + ..\..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll + + + ..\..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll + + + + + ..\..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll + + + ..\..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll + + + ..\..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll + + + + + + + + + Global.asax + + + + + + + + + + + + + + + + + + + + + + + Web.config + + + Web.config + + + + + + + + + + + + + {2efffc6b-e642-477f-b537-4241ebd93410} + JavaScriptEngineSwitcher.ChakraCore + + + {13559975-f99d-4b93-bf46-227c0b6e0dfb} + JavaScriptEngineSwitcher.Core + + + {d31b5a77-8018-4d76-b372-325564385b2d} + JavaScriptEngineSwitcher.Jurassic + + + {b3c4aa95-2227-47dd-b58c-22fa589cb28d} + JavaScriptEngineSwitcher.Msie + + + {238d7e69-7052-4dfc-83ef-79d3d124c12b} + JavaScriptEngineSwitcher.Vroom + + + {c434e1b5-0463-4e58-8336-87f822d07b41} + JavaScriptEngineSwitcher.Sample.Logic + + + {62f0c0ad-15bc-42ce-9bd2-49f506feaa54} + JavaScriptEngineSwitcher.Sample.Resources + + + + + + + + + + + + + + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + + + + + + + True + True + 6148 + / + http://localhost:6148/ + False + False + + + False + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Properties/AssemblyInfo.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..2b4ecedb --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Properties/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("JavaScriptEngineSwitcher.Sample.AspNet4")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JS Engine Switcher: Sample ASP.NET MVC 4 Site")] +[assembly: AssemblyCopyright("Copyright © 2013-2025 Andrey Taritsyn")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] +[assembly: Guid("39487053-b459-4433-ae93-e00affc653c6")] + +[assembly: AssemblyVersion("3.30.2.0")] +[assembly: AssemblyFileVersion("3.30.2.0")] \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Home/Contact.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Home/Contact.cshtml new file mode 100644 index 00000000..2154ade2 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Home/Contact.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Contact"; +} + +
+

@ViewBag.Title

+ @Html.Raw(ViewBag.Body) +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Home/Demo.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Home/Demo.cshtml new file mode 100644 index 00000000..7acbc883 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Home/Demo.cshtml @@ -0,0 +1,76 @@ +@using JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models +@using JavaScriptEngineSwitcher.Sample.Resources + +@model JsEvaluationViewModel + +@{ + ViewBag.Title = "Demo"; +} + +

@ViewBag.Title

+ +@using (Html.BeginForm("Demo", "Home", FormMethod.Post, new { data_form_type = "evaluation-form" })) +{ +
+
+
+
+ @Html.CustomLabelFor(m => m.EngineName, true, true) + @(Html.DropDownListFor(m => m.EngineName, Model.AvailableEngineList, + new { @class = "form-control" }) + ) +
+ +
+ @Html.CustomLabelFor(m => m.Expression, true, true) +
+ @(Html.TextAreaFor(m => m.Expression, + new + { + @class = "form-control evaluation-input-field", + data_control_type = "evaluation-input-field", + rows = 12, + cols = 80 + }) + ) +
+ @Html.ValidationMessageFor(m => m.Expression) +
+
+ +
+
+ + @if (Model.Result != null) + { +
+ @if (Model.Result.Errors.Count == 0) + { +
+ @Html.CustomLabelFor(m => m.Result.Value, true, true) + @(Html.TextAreaFor(m => m.Result.Value, + new + { + @class = "form-control evaluation-output-field", + data_control_type = "evaluation-output-field", + rows = 12, + cols = 80, + @readonly = "readonly" + }) + ) +
+ } + else + { + @Html.Partial("_JsEvaluationErrorList", Model.Result.Errors) + } +
+ } +
+
+} + +@section Scripts { + +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Home/Index.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Home/Index.cshtml new file mode 100644 index 00000000..ee77a459 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = string.Empty; +} + +
+

Project Description

+ @Html.Raw(ViewBag.Body) +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Shared/Error.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Shared/Error.cshtml new file mode 100644 index 00000000..0971c1d7 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Shared/Error.cshtml @@ -0,0 +1,10 @@ +@model HandleErrorInfo + +@{ + ViewBag.Title = "Error"; +} + +
+

Error.

+

An error occurred while processing your request.

+
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Shared/_JsEvaluationErrorList.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Shared/_JsEvaluationErrorList.cshtml new file mode 100644 index 00000000..923a65e8 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Shared/_JsEvaluationErrorList.cshtml @@ -0,0 +1,23 @@ +@using JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models + +@model IList + +

Found @Model.Count error(s):

+
    + @foreach (var error in Model) + { +
  • + @error.EngineFullName
    + @if (error.LineNumber > 0) + { + Line @error.LineNumber, Column @error.ColumnNumber
    + } + @Html.EncodedReplace(@error.Message, "\n\r?", "
    ") + @if (!string.IsNullOrWhiteSpace(error.SourceFragment)) + { +
    @error.SourceFragment
    + } +
  • + } +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Shared/_Layout.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..7a886756 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Shared/_Layout.cshtml @@ -0,0 +1,78 @@ + + + + + + + + + @if (!string.IsNullOrEmpty(ViewBag.Title)) {<text>@ViewBag.Title | </text>}JavaScriptEngineSwitcher Sample ASP.NET 4.0 MVC 4 Site + + + + + + +
+ + + + + +
+
+ @RenderBody() +
+
+ + +
+
+ + + + + + + + + + + + + @RenderSection("scripts", required: false) + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Web.config b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Web.config new file mode 100644 index 00000000..8db224c1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/Web.config @@ -0,0 +1,44 @@ + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/_ViewStart.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/_ViewStart.cshtml new file mode 100644 index 00000000..25291bc2 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "~/Views/Shared/_Layout.cshtml"; +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Web.Debug.config b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Web.Debug.config new file mode 100644 index 00000000..8cf71e0a --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Web.Debug.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Web.Release.config b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Web.Release.config new file mode 100644 index 00000000..b1b28f61 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Web.Release.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Web.config b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Web.config new file mode 100644 index 00000000..3e44d3df --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/Web.config @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/bower.json b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/bower.json new file mode 100644 index 00000000..bbac5913 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/bower.json @@ -0,0 +1,11 @@ +{ + "name": "jsengineswitcher.sample.aspnet4.mvc4", + "dependencies": { + "modernizr": "2.8.3", + "jquery-compat": "jquery#1.10.2", + "jquery": "jquery#2.0.3", + "jquery-validation": "1.13.1", + "jquery-validation-unobtrusive": "3.2.3", + "bootstrap": "3.3.0" + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/build-frontend.cmd b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/build-frontend.cmd new file mode 100644 index 00000000..b36fa314 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/build-frontend.cmd @@ -0,0 +1,41 @@ +@echo off +setlocal + +::-------------------------------------------------------------------------------- +:: Build +::-------------------------------------------------------------------------------- + +echo Starting to build the frontend for ASP.NET MVC 4 sample ... +echo. + +echo Installing Node.js packages ... +echo. +call npm install +if errorlevel 1 goto error +echo. + +echo Installing Bower packages ... +echo. +call bower install +if errorlevel 1 goto error +echo. + +echo Building client-side assets ... +echo. +call gulp +if errorlevel 1 goto error +echo. + +::-------------------------------------------------------------------------------- +:: Exit +::-------------------------------------------------------------------------------- + +echo Succeeded! +goto exit + +:error +echo *** Error: The previous step failed! + +:exit +cd ../../ +endlocal \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/build-frontend.sh b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/build-frontend.sh new file mode 100644 index 00000000..112d2a8d --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/build-frontend.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +function handle_error() +{ + _RET=${PIPESTATUS[0]} + if [[ $_RET != 0 ]]; then + echo "*** Error: The previous step failed!" + + cd ../../ + exit $_RET + fi +} + +echo "Starting to build the frontend for ASP.NET MVC 4 sample ..." +echo "" + +echo "Installing Node.js packages ..." +echo "" +npm install +handle_error +echo "" + +echo "Installing Bower packages ..." +echo "" +bower install +handle_error +echo "" + +echo "Building client-side assets ..." +echo "" +gulp +handle_error +echo "" + +echo "Succeeded!" + +cd ../../ +exit $_RET \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/gulpfile.js b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/gulpfile.js new file mode 100644 index 00000000..a0deea2a --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/gulpfile.js @@ -0,0 +1,130 @@ +/*global require, exports */ +/*jshint esversion: 6 */ +const WEB_ROOT_PATH = "."; +const BOWER_DIR_PATH = "lib"; +const STYLE_DIR_PATH = WEB_ROOT_PATH + '/styles'; +const SCRIPT_DIR_PATH = WEB_ROOT_PATH + '/scripts'; + +// include plug-ins +let { src, dest, series, parallel, watch } = require('gulp'); +let del = require('del'); +let sourcemaps = require('gulp-sourcemaps'); +let rename = require('gulp-rename'); +let concat = require('gulp-concat'); +let less = require('gulp-less'); +let autoprefixer = require('gulp-autoprefixer'); +let cleanCss = require('gulp-clean-css'); +let uglify = require('gulp-uglify'); + +//#region Clean +//#region Clean builded assets +function cleanBuildedStyles() { + return del([STYLE_DIR_PATH + '/build/*']); +} + +function cleanBuildedScripts() { + return del([SCRIPT_DIR_PATH + '/build/*']); +} + +let cleanBuildedAssets = parallel(cleanBuildedStyles, cleanBuildedScripts); +//#endregion +//#endregion + +//#region Build assets +//#region Build styles +let autoprefixerOptions = { + overrideBrowserslist: ['> 1%', 'last 3 versions', 'Firefox ESR', 'Opera 12.1'], + cascade: true +}; +let cssCleanOptions = { specialComments: '*' }; +let cssRenameOptions = { extname: '.min.css' }; + +function buildCommonStyles() { + return src([STYLE_DIR_PATH + '/app.less']) + .pipe(sourcemaps.init()) + .pipe(less({ + relativeUrls: true, + rootpath: '/styles/' + })) + .pipe(autoprefixer(autoprefixerOptions)) + .pipe(sourcemaps.write('./')) + .pipe(dest(STYLE_DIR_PATH + '/build')) + .pipe(sourcemaps.init({ loadMaps: true })) + .pipe(concat('common-styles.css')) + .pipe(cleanCss(cssCleanOptions)) + .pipe(rename(cssRenameOptions)) + .pipe(sourcemaps.write('./')) + .pipe(dest(STYLE_DIR_PATH + '/build')) + ; +} + +let buildStyles = buildCommonStyles; +//#endregion + +//#region Build scripts +let jsConcatOptions = { newLine: ';' }; +let jsUglifyOptions = { + output: { comments: /^!/ } +}; +let jsRenameOptions = { extname: '.min.js' }; + +function buildModernizrScripts() { + return src([BOWER_DIR_PATH + '/modernizr/modernizr.js']) + .pipe(sourcemaps.init()) + .pipe(uglify(jsUglifyOptions)) + .pipe(rename(jsRenameOptions)) + .pipe(sourcemaps.write('./')) + .pipe(dest(SCRIPT_DIR_PATH + '/build')) + ; +} + +function buildCommonScripts() { + return src([SCRIPT_DIR_PATH + '/common.js']) + .pipe(sourcemaps.init({ loadMaps: true })) + .pipe(rename({ basename: 'common-scripts' })) + .pipe(uglify(jsUglifyOptions)) + .pipe(rename(jsRenameOptions)) + .pipe(sourcemaps.write('./')) + .pipe(dest(SCRIPT_DIR_PATH + '/build')) + ; +} + +function buildEvaluationFormScripts() { + return src([BOWER_DIR_PATH + '/jquery-validation/dist/jquery.validate.js', + BOWER_DIR_PATH + '/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js', + BOWER_DIR_PATH + '/bootstrap/js/button.js', + SCRIPT_DIR_PATH + '/evaluation-form.js']) + .pipe(sourcemaps.init({ loadMaps: true })) + .pipe(concat('evaluation-form-scripts.js', jsConcatOptions)) + .pipe(uglify(jsUglifyOptions)) + .pipe(rename(jsRenameOptions)) + .pipe(sourcemaps.write('./')) + .pipe(dest(SCRIPT_DIR_PATH + '/build')) + ; +} + +let buildScripts = parallel(buildModernizrScripts, buildCommonScripts, buildEvaluationFormScripts); +//#endregion + +let buildAssets = parallel(buildStyles, buildScripts); +//#endregion + +//#region Watch assets +function watchStyles() { + return watch([STYLE_DIR_PATH + '/**/*.{less,css}', '!' + STYLE_DIR_PATH + '/build/**/*.*'], + buildStyles); +} + +function watchScripts() { + return watch([SCRIPT_DIR_PATH + '/**/*.js', '!' + SCRIPT_DIR_PATH + '/build/**/*.*'], + buildScripts); +} + +let watchAssets = parallel(watchStyles, watchScripts); +//#endregion + +// Export tasks +exports.cleanBuildedAssets = cleanBuildedAssets; +exports.buildAssets = buildAssets; +exports.watchAssets = watchAssets; +exports.default = series(cleanBuildedAssets, buildAssets); \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/0.gif b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/0.gif new file mode 100644 index 00000000..35d42e80 Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/0.gif differ diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/clear-text.png b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/clear-text.png new file mode 100644 index 00000000..9d217b7f Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/clear-text.png differ diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/icons/32x32/social-media-icons-32.png b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/icons/32x32/social-media-icons-32.png new file mode 100644 index 00000000..30d1c781 Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/icons/32x32/social-media-icons-32.png differ diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/icons/48x48/error.png b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/icons/48x48/error.png new file mode 100644 index 00000000..3d6185f7 Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/icons/48x48/error.png differ diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/jsengineswitcher-logo.png b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/jsengineswitcher-logo.png new file mode 100644 index 00000000..12ed97e0 Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/images/jsengineswitcher-logo.png differ diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/package.json b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/package.json new file mode 100644 index 00000000..98d601d7 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/package.json @@ -0,0 +1,16 @@ +{ + "name": "jsengineswitcher.sample.aspnet4.mvc4", + "private": true, + "version": "3.30.2", + "devDependencies": { + "gulp": "4.0.2", + "del": "5.1.0", + "gulp-sourcemaps": "2.6.5", + "gulp-rename": "2.0.0", + "gulp-concat": "2.6.1", + "gulp-less": "4.0.1", + "gulp-autoprefixer": "7.0.1", + "gulp-clean-css": "4.2.0", + "gulp-uglify": "3.0.2" + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/packages.config b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/packages.config new file mode 100644 index 00000000..6b274db9 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/scripts/_references.js b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/scripts/_references.js new file mode 100644 index 00000000..2b54eea8 Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/scripts/_references.js differ diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/scripts/common.js b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/scripts/common.js new file mode 100644 index 00000000..c94bf102 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/scripts/common.js @@ -0,0 +1,29 @@ +var jsEngineSwitcher; + +(function (jsEngineSwitcher, undefined) { + "use strict"; + + jsEngineSwitcher.registerNamespace = function (namespaceString) { + var parts = namespaceString.split("."), + parent = jsEngineSwitcher, + i + ; + + if (parts[0] === "jsEngineSwitcher") { + parts = parts.slice(1); + } + + for (i = 0; i < parts.length; i += 1) { + if (typeof parent[parts[i]] === "undefined") { + parent[parts[i]] = {}; + } + parent = parent[parts[i]]; + } + + return parent; + }; + + jsEngineSwitcher.hasScrollbar = function(elem) { + return (elem.clientHeight < elem.scrollHeight); + }; +}(jsEngineSwitcher = jsEngineSwitcher || {})); \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/scripts/evaluation-form.js b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/scripts/evaluation-form.js new file mode 100644 index 00000000..da1f0199 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/scripts/evaluation-form.js @@ -0,0 +1,83 @@ +(function (jsEngineSwitcher, $, undefined) { + "use strict"; + + var $evaluationForm, + $evaluationInputField, + $evaluationInputClearButton, + $evaluateButton + ; + + $(function () { + $evaluationForm = $("form[data-form-type='evaluation-form']"); + $evaluationInputField = $(":input[data-control-type='evaluation-input-field']", $evaluationForm); + $evaluationInputClearButton = $("
"); + $evaluateButton = $(":input[data-control-type='evaluate-button']", $evaluationForm); + + $evaluationForm.on("submit", onEvaluationFormSubmitHandler); + + $evaluationInputClearButton.on("click", onEvaluationInputClearButtonClickHandler); + $evaluationInputField.parent().append($evaluationInputClearButton); + refreshEvaluationInputClearButton(); + $evaluationInputField + .on("input propertychange keydown keyup paste", onEvaluationInputFieldChangeHandler) + ; + + $evaluateButton.removeAttr("disabled"); + }); + + $(window).unload(function() { + $evaluationForm.off("submit", onEvaluationFormSubmitHandler); + + $evaluationInputClearButton + .off("click", onEvaluationInputClearButtonClickHandler) + .remove() + ; + + $evaluationInputField + .off("input propertychange keydown keyup paste", onEvaluationInputFieldChangeHandler) + ; + + $evaluationForm = null; + $evaluationInputField = null; + $evaluationInputClearButton = null; + $evaluateButton = null; + }); + + var refreshEvaluationInputClearButton = function() { + if ($.trim($evaluationInputField.val()).length > 0) { + $evaluationInputClearButton.show(); + } else { + $evaluationInputClearButton.hide(); + } + + if (jsEngineSwitcher.hasScrollbar($evaluationInputField.get(0))) { + $evaluationInputClearButton.addClass("with-scrollbar"); + } + else { + $evaluationInputClearButton.removeClass("with-scrollbar"); + } + }; + + var onEvaluationFormSubmitHandler = function () { + var $form = $(this); + if ($form.valid()) { + $evaluateButton.attr("disabled", "disabled"); + $("textarea[data-control-type='evaluation-output-field']", $form).val(''); + + return true; + } + + return false; + }; + + var onEvaluationInputFieldChangeHandler = function () { + refreshEvaluationInputClearButton(); + }; + + var onEvaluationInputClearButtonClickHandler = function() { + $evaluationInputField.val(""); + + var $button = $(this); + $button.hide(); + }; +}(jsEngineSwitcher, jQuery)); \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/app.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/app.less new file mode 100644 index 00000000..c41a9315 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/app.less @@ -0,0 +1,71 @@ +/*! + * Bootstrap v3.3.0 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +// Core variables and mixins +@import "bootstrap-custom-variables.less"; +@import "../lib/bootstrap/less/mixins.less"; + +// Reset +@import "../lib/bootstrap/less/normalize.less"; +//@import "../lib/bootstrap/less/print.less"; +//@import "../lib/bootstrap/less/glyphicons.less"; + +// Core CSS +@import "../lib/bootstrap/less/scaffolding.less"; +@import "../lib/bootstrap/less/type.less"; +@import "../lib/bootstrap/less/code.less"; +//@import "../lib/bootstrap/less/grid.less"; +//@import "../lib/bootstrap/less/tables.less"; +@import "../lib/bootstrap/less/forms.less"; +//@import "../lib/bootstrap/less/buttons.less"; + +// Components +//@import "../lib/bootstrap/less/component-animations.less"; +//@import "../lib/bootstrap/less/dropdowns.less"; +//@import "../lib/bootstrap/less/button-groups.less"; +//@import "../lib/bootstrap/less/input-groups.less"; +@import "../lib/bootstrap/less/navs.less"; +@import "../lib/bootstrap/less/navbar.less"; +//@import "../lib/bootstrap/less/breadcrumbs.less"; +//@import "../lib/bootstrap/less/pagination.less"; +//@import "../lib/bootstrap/less/pager.less"; +//@import "../lib/bootstrap/less/labels.less"; +//@import "../lib/bootstrap/less/badges.less"; +//@import "../lib/bootstrap/less/jumbotron.less"; +//@import "../lib/bootstrap/less/thumbnails.less"; +//@import "../lib/bootstrap/less/alerts.less"; +//@import "../lib/bootstrap/less/progress-bars.less"; +//@import "../lib/bootstrap/less/media.less"; +//@import "../lib/bootstrap/less/list-group.less"; +//@import "../lib/bootstrap/less/panels.less"; +//@import "../lib/bootstrap/less/responsive-embed.less"; +//@import "../lib/bootstrap/less/wells.less"; +//@import "../lib/bootstrap/less/close.less"; + +// Components w/ JavaScript +//@import "../lib/bootstrap/less/modals.less"; +//@import "../lib/bootstrap/less/tooltip.less"; +//@import "../lib/bootstrap/less/popovers.less"; +//@import "../lib/bootstrap/less/carousel.less"; + +// Utility classes +@import "../lib/bootstrap/less/utilities.less"; +//@import "../lib/bootstrap/less/responsive-utilities.less"; + +// JavaScriptEngineSwitcher specs +@import "variables.less"; +@import "mixins.less"; + +@import "type.less"; +@import "code.less"; +@import "forms.less"; +@import "buttons.less"; +@import "icons.less"; +@import "navbar.less"; + +@import "layout.less"; +@import "evaluation-form.less"; +@import "page-socials.less"; \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/bootstrap-custom-variables.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/bootstrap-custom-variables.less new file mode 100644 index 00000000..0be2aa6f --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/bootstrap-custom-variables.less @@ -0,0 +1,856 @@ +// +// Variables +// -------------------------------------------------- + + +//== Colors +// +//## Gray and brand colors for use across Bootstrap. + +@gray-base: #323330; +@gray-darker: lighten(@gray-base, 13.5%); // #222 +@gray-dark: lighten(@gray-base, 20%); // #333 +@gray: lighten(@gray-base, 33.5%); // #555 +@gray-light: lighten(@gray-base, 46.7%); // #777 +@gray-lighter: #e3e3e3; + +@brand-primary: #428bca; +@brand-success: #5cb85c; +@brand-info: #5bc0de; +@brand-warning: #f0ad4e; +@brand-danger: #d9534f; + + +//== Scaffolding +// +//## Settings for some of the most global styles. + +//** Background color for ``. +@body-bg: #fff; +//** Global text color on ``. +@text-color: #1d0d0d; + +//** Global textual link color. +@link-color: @brand-primary; +//** Link hover color set via `darken()` function. +@link-hover-color: darken(@link-color, 15%); +//** Link hover decoration. +@link-hover-decoration: underline; + + +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. + +@font-family-sans-serif: "Helvetica Neue", Arial, sans-serif; +@font-family-serif: Georgia, "Times New Roman", Times, serif; +//** Default monospace fonts for ``, ``, and `
`.
+@font-family-monospace:   Consolas, "Courier New", monospace;
+@font-family-base:        @font-family-sans-serif;
+
+@font-size-base:          14px;
+@font-size-large:         ceil((@font-size-base * 1.25)); // ~18px
+@font-size-small:         ceil((@font-size-base * 0.85)); // ~12px
+
+@font-size-h1:            floor(@font-size-base * 3);
+@font-size-h2:            @font-size-base * 2.5;
+@font-size-h3:            ceil(@font-size-base * 1.75);
+@font-size-h4:            ceil(@font-size-base * 1.25);
+@font-size-h5:            @font-size-base;
+@font-size-h6:            ceil(@font-size-base * 0.85);
+
+//** Unit-less `line-height` for use in components like buttons.
+@line-height-base:        1.428571429; // 20/14
+//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
+@line-height-computed:    floor((@font-size-base * @line-height-base)); // ~20px
+
+//** By default, this inherits from the ``.
+@headings-font-family:    "Open Sans", sans-serif;
+@headings-font-weight:    normal;
+@headings-line-height:    1.1;
+@headings-color:          inherit;
+
+
+//== Iconography
+//
+//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
+
+//** Load fonts from this directory.
+@icon-font-path:          "../fonts/";
+//** File name for all font files.
+@icon-font-name:          "glyphicons-halflings-regular";
+//** Element ID within SVG icon file.
+@icon-font-svg-id:        "glyphicons_halflingsregular";
+
+
+//== Components
+//
+//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
+
+@padding-base-vertical:     4px;
+@padding-base-horizontal:   6px;
+
+@padding-large-vertical:    10px;
+@padding-large-horizontal:  16px;
+
+@padding-small-vertical:    5px;
+@padding-small-horizontal:  10px;
+
+@padding-xs-vertical:       1px;
+@padding-xs-horizontal:     5px;
+
+@line-height-large:         1.33;
+@line-height-small:         1.5;
+
+@border-radius-base:        4px;
+@border-radius-large:       6px;
+@border-radius-small:       3px;
+
+//** Global color for active items (e.g., navs or dropdowns).
+@component-active-color:    #fff;
+//** Global background color for active items (e.g., navs or dropdowns).
+@component-active-bg:       @brand-primary;
+
+//** Width of the `border` for generating carets that indicator dropdowns.
+@caret-width-base:          4px;
+//** Carets increase slightly in size for larger components.
+@caret-width-large:         5px;
+
+
+//== Tables
+//
+//## Customizes the `.table` component with basic values, each used across all table variations.
+
+//** Padding for ``s and ``s.
+@table-cell-padding:            8px;
+//** Padding for cells in `.table-condensed`.
+@table-condensed-cell-padding:  5px;
+
+//** Default background color used for all tables.
+@table-bg:                      transparent;
+//** Background color used for `.table-striped`.
+@table-bg-accent:               #f9f9f9;
+//** Background color used for `.table-hover`.
+@table-bg-hover:                #f5f5f5;
+@table-bg-active:               @table-bg-hover;
+
+//** Border color for table and cell borders.
+@table-border-color:            #ddd;
+
+
+//== Buttons
+//
+//## For each of Bootstrap's buttons, define text, background and border color.
+
+@btn-font-weight:                normal;
+
+@btn-default-color:              #333;
+@btn-default-bg:                 @gray-lighter;
+@btn-default-border:             @gray-lighter;
+
+@btn-primary-color:              #fff;
+@btn-primary-bg:                 @brand-primary;
+@btn-primary-border:             darken(@btn-primary-bg, 5%);
+
+@btn-success-color:              #fff;
+@btn-success-bg:                 @brand-success;
+@btn-success-border:             darken(@btn-success-bg, 5%);
+
+@btn-info-color:                 #fff;
+@btn-info-bg:                    @brand-info;
+@btn-info-border:                darken(@btn-info-bg, 5%);
+
+@btn-warning-color:              #fff;
+@btn-warning-bg:                 @brand-warning;
+@btn-warning-border:             darken(@btn-warning-bg, 5%);
+
+@btn-danger-color:               #fff;
+@btn-danger-bg:                  @brand-danger;
+@btn-danger-border:              darken(@btn-danger-bg, 5%);
+
+@btn-link-disabled-color:        @gray-light;
+
+
+//== Forms
+//
+//##
+
+//** `` background color
+@input-bg:                       #fff;
+//** `` background color
+@input-bg-disabled:              @gray-lighter;
+
+//** Text color for ``s
+@input-color:                    @gray;
+//** `` border color
+@input-border:                   #b2b2b2;
+
+// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
+//** Default `.form-control` border radius
+@input-border-radius:            @border-radius-base;
+//** Large `.form-control` border radius
+@input-border-radius-large:      @border-radius-large;
+//** Small `.form-control` border radius
+@input-border-radius-small:      @border-radius-small;
+
+//** Border color for inputs on focus
+@input-border-focus:             #d4b33a;
+
+//** Placeholder text color
+@input-color-placeholder:        #999;
+
+//** Default `.form-control` height
+@input-height-base:              (@line-height-computed + (@padding-base-vertical * 2) + 2);
+//** Large `.form-control` height
+@input-height-large:             (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
+//** Small `.form-control` height
+@input-height-small:             (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
+
+@legend-color:                   @gray-dark;
+@legend-border-color:            #e5e5e5;
+
+//** Background color for textual input addons
+@input-group-addon-bg:           @gray-lighter;
+//** Border color for textual input addons
+@input-group-addon-border-color: @input-border;
+
+//** Disabled cursor for form controls and buttons.
+@cursor-disabled:                not-allowed;
+
+
+//== Dropdowns
+//
+//## Dropdown menu container and contents.
+
+//** Background for the dropdown menu.
+@dropdown-bg:                    #fff;
+//** Dropdown menu `border-color`.
+@dropdown-border:                rgba(0,0,0,.15);
+//** Dropdown menu `border-color` **for IE8**.
+@dropdown-fallback-border:       #ccc;
+//** Divider color for between dropdown items.
+@dropdown-divider-bg:            #e5e5e5;
+
+//** Dropdown link text color.
+@dropdown-link-color:            @gray-dark;
+//** Hover color for dropdown links.
+@dropdown-link-hover-color:      darken(@gray-dark, 5%);
+//** Hover background for dropdown links.
+@dropdown-link-hover-bg:         #f5f5f5;
+
+//** Active dropdown menu item text color.
+@dropdown-link-active-color:     @component-active-color;
+//** Active dropdown menu item background color.
+@dropdown-link-active-bg:        @component-active-bg;
+
+//** Disabled dropdown menu item background color.
+@dropdown-link-disabled-color:   @gray-light;
+
+//** Text color for headers within dropdown menus.
+@dropdown-header-color:          @gray-light;
+
+//** Deprecated `@dropdown-caret-color` as of v3.1.0
+@dropdown-caret-color:           #000;
+
+
+//-- Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+//
+// Note: These variables are not generated into the Customizer.
+
+@zindex-navbar:            1000;
+@zindex-dropdown:          1000;
+@zindex-popover:           1060;
+@zindex-tooltip:           1070;
+@zindex-navbar-fixed:      1030;
+@zindex-modal:             1040;
+
+
+//== Media queries breakpoints
+//
+//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
+
+// Extra small screen / phone
+//** Deprecated `@screen-xs` as of v3.0.1
+@screen-xs:                  480px;
+//** Deprecated `@screen-xs-min` as of v3.2.0
+@screen-xs-min:              @screen-xs;
+//** Deprecated `@screen-phone` as of v3.0.1
+@screen-phone:               @screen-xs-min;
+
+// Small screen / tablet
+//** Deprecated `@screen-sm` as of v3.0.1
+@screen-sm:                  768px;
+@screen-sm-min:              @screen-sm;
+//** Deprecated `@screen-tablet` as of v3.0.1
+@screen-tablet:              @screen-sm-min;
+
+// Medium screen / desktop
+//** Deprecated `@screen-md` as of v3.0.1
+@screen-md:                  992px;
+@screen-md-min:              @screen-md;
+//** Deprecated `@screen-desktop` as of v3.0.1
+@screen-desktop:             @screen-md-min;
+
+// Large screen / wide desktop
+//** Deprecated `@screen-lg` as of v3.0.1
+@screen-lg:                  1200px;
+@screen-lg-min:              @screen-lg;
+//** Deprecated `@screen-lg-desktop` as of v3.0.1
+@screen-lg-desktop:          @screen-lg-min;
+
+// So media queries don't overlap when required, provide a maximum
+@screen-xs-max:              (@screen-sm-min - 1);
+@screen-sm-max:              (@screen-md-min - 1);
+@screen-md-max:              (@screen-lg-min - 1);
+
+
+//== Grid system
+//
+//## Define your custom responsive grid.
+
+//** Number of columns in the grid.
+@grid-columns:              12;
+//** Padding between columns. Gets divided in half for the left and right.
+@grid-gutter-width:         30px;
+// Navbar collapse
+//** Point at which the navbar becomes uncollapsed.
+@grid-float-breakpoint:     @screen-sm-min;
+//** Point at which the navbar begins collapsing.
+@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
+
+
+//== Container sizes
+//
+//## Define the maximum width of `.container` for different screen sizes.
+
+// Small screen / tablet
+@container-tablet:             (720px + @grid-gutter-width);
+//** For `@screen-sm-min` and up.
+@container-sm:                 @container-tablet;
+
+// Medium screen / desktop
+@container-desktop:            (940px + @grid-gutter-width);
+//** For `@screen-md-min` and up.
+@container-md:                 @container-desktop;
+
+// Large screen / wide desktop
+@container-large-desktop:      (1140px + @grid-gutter-width);
+//** For `@screen-lg-min` and up.
+@container-lg:                 @container-large-desktop;
+
+
+//== Navbar
+//
+//##
+
+// Basics of a navbar
+@navbar-height:                    100px;
+@navbar-margin-bottom:             0;
+@navbar-border-radius:             @border-radius-base;
+@navbar-padding-horizontal:        0;
+@navbar-padding-vertical:          ((@navbar-height - @line-height-computed) / 2);
+@navbar-collapse-max-height:       340px;
+
+@navbar-default-color:             @gray-base;
+@navbar-default-bg:                #f0db4f;
+@navbar-default-border:            #f0db4f;
+
+// Navbar links
+@navbar-default-link-color:                @navbar-default-color;
+@navbar-default-link-hover-color:          @gray-dark;
+@navbar-default-link-hover-bg:             transparent;
+@navbar-default-link-active-color:         @navbar-default-color;
+@navbar-default-link-active-bg:            transparent;
+@navbar-default-link-disabled-color:       #ccc;
+@navbar-default-link-disabled-bg:          transparent;
+
+// Navbar brand label
+@navbar-default-brand-color:               @navbar-default-link-color;
+@navbar-default-brand-hover-color:         darken(@navbar-default-brand-color, 10%);
+@navbar-default-brand-hover-bg:            transparent;
+
+// Navbar toggle
+@navbar-default-toggle-hover-bg:           #ddd;
+@navbar-default-toggle-icon-bar-bg:        @gray-dark;
+@navbar-default-toggle-border-color:       #ddd;
+
+
+// Inverted navbar
+// Reset inverted navbar basics
+@navbar-inverse-color:                      lighten(@gray-light, 15%);
+@navbar-inverse-bg:                         #222;
+@navbar-inverse-border:                     darken(@navbar-inverse-bg, 10%);
+
+// Inverted navbar links
+@navbar-inverse-link-color:                 lighten(@gray-light, 15%);
+@navbar-inverse-link-hover-color:           #fff;
+@navbar-inverse-link-hover-bg:              transparent;
+@navbar-inverse-link-active-color:          @navbar-inverse-link-hover-color;
+@navbar-inverse-link-active-bg:             darken(@navbar-inverse-bg, 10%);
+@navbar-inverse-link-disabled-color:        #444;
+@navbar-inverse-link-disabled-bg:           transparent;
+
+// Inverted navbar brand label
+@navbar-inverse-brand-color:                @navbar-inverse-link-color;
+@navbar-inverse-brand-hover-color:          #fff;
+@navbar-inverse-brand-hover-bg:             transparent;
+
+// Inverted navbar toggle
+@navbar-inverse-toggle-hover-bg:            #333;
+@navbar-inverse-toggle-icon-bar-bg:         #fff;
+@navbar-inverse-toggle-border-color:        #333;
+
+
+//== Navs
+//
+//##
+
+//=== Shared nav styles
+@nav-link-padding:                          10px 15px;
+@nav-link-hover-bg:                         @gray-lighter;
+
+@nav-disabled-link-color:                   @gray-light;
+@nav-disabled-link-hover-color:             @gray-light;
+
+//== Tabs
+@nav-tabs-border-color:                     #ddd;
+
+@nav-tabs-link-hover-border-color:          @gray-lighter;
+
+@nav-tabs-active-link-hover-bg:             @body-bg;
+@nav-tabs-active-link-hover-color:          @gray;
+@nav-tabs-active-link-hover-border-color:   #ddd;
+
+@nav-tabs-justified-link-border-color:            #ddd;
+@nav-tabs-justified-active-link-border-color:     @body-bg;
+
+//== Pills
+@nav-pills-border-radius:                   @border-radius-base;
+@nav-pills-active-link-hover-bg:            @component-active-bg;
+@nav-pills-active-link-hover-color:         @component-active-color;
+
+
+//== Pagination
+//
+//##
+
+@pagination-color:                     @link-color;
+@pagination-bg:                        #fff;
+@pagination-border:                    #ddd;
+
+@pagination-hover-color:               @link-hover-color;
+@pagination-hover-bg:                  @gray-lighter;
+@pagination-hover-border:              #ddd;
+
+@pagination-active-color:              #fff;
+@pagination-active-bg:                 @brand-primary;
+@pagination-active-border:             @brand-primary;
+
+@pagination-disabled-color:            @gray-light;
+@pagination-disabled-bg:               #fff;
+@pagination-disabled-border:           #ddd;
+
+
+//== Pager
+//
+//##
+
+@pager-bg:                             @pagination-bg;
+@pager-border:                         @pagination-border;
+@pager-border-radius:                  15px;
+
+@pager-hover-bg:                       @pagination-hover-bg;
+
+@pager-active-bg:                      @pagination-active-bg;
+@pager-active-color:                   @pagination-active-color;
+
+@pager-disabled-color:                 @pagination-disabled-color;
+
+
+//== Jumbotron
+//
+//##
+
+@jumbotron-padding:              30px;
+@jumbotron-color:                inherit;
+@jumbotron-bg:                   @gray-lighter;
+@jumbotron-heading-color:        inherit;
+@jumbotron-font-size:            ceil((@font-size-base * 1.5));
+
+
+//== Form states and alerts
+//
+//## Define colors for form feedback states and, by default, alerts.
+
+@state-success-text:             #3c763d;
+@state-success-bg:               #dff0d8;
+@state-success-border:           darken(spin(@state-success-bg, -10), 5%);
+
+@state-info-text:                #31708f;
+@state-info-bg:                  #d9edf7;
+@state-info-border:              darken(spin(@state-info-bg, -10), 7%);
+
+@state-warning-text:             #8a6d3b;
+@state-warning-bg:               #fcf8e3;
+@state-warning-border:           darken(spin(@state-warning-bg, -10), 5%);
+
+@state-danger-text:              #a94442;
+@state-danger-bg:                #f2dede;
+@state-danger-border:            darken(spin(@state-danger-bg, -10), 5%);
+
+
+//== Tooltips
+//
+//##
+
+//** Tooltip max width
+@tooltip-max-width:           200px;
+//** Tooltip text color
+@tooltip-color:               #fff;
+//** Tooltip background color
+@tooltip-bg:                  #000;
+@tooltip-opacity:             .9;
+
+//** Tooltip arrow width
+@tooltip-arrow-width:         5px;
+//** Tooltip arrow color
+@tooltip-arrow-color:         @tooltip-bg;
+
+
+//== Popovers
+//
+//##
+
+//** Popover body background color
+@popover-bg:                          #fff;
+//** Popover maximum width
+@popover-max-width:                   276px;
+//** Popover border color
+@popover-border-color:                rgba(0,0,0,.2);
+//** Popover fallback border color
+@popover-fallback-border-color:       #ccc;
+
+//** Popover title background color
+@popover-title-bg:                    darken(@popover-bg, 3%);
+
+//** Popover arrow width
+@popover-arrow-width:                 10px;
+//** Popover arrow color
+@popover-arrow-color:                 @popover-bg;
+
+//** Popover outer arrow width
+@popover-arrow-outer-width:           (@popover-arrow-width + 1);
+//** Popover outer arrow color
+@popover-arrow-outer-color:           fadein(@popover-border-color, 5%);
+//** Popover outer arrow fallback color
+@popover-arrow-outer-fallback-color:  darken(@popover-fallback-border-color, 20%);
+
+
+//== Labels
+//
+//##
+
+//** Default label background color
+@label-default-bg:            @gray-light;
+//** Primary label background color
+@label-primary-bg:            @brand-primary;
+//** Success label background color
+@label-success-bg:            @brand-success;
+//** Info label background color
+@label-info-bg:               @brand-info;
+//** Warning label background color
+@label-warning-bg:            @brand-warning;
+//** Danger label background color
+@label-danger-bg:             @brand-danger;
+
+//** Default label text color
+@label-color:                 #fff;
+//** Default text color of a linked label
+@label-link-hover-color:      #fff;
+
+
+//== Modals
+//
+//##
+
+//** Padding applied to the modal body
+@modal-inner-padding:         15px;
+
+//** Padding applied to the modal title
+@modal-title-padding:         15px;
+//** Modal title line-height
+@modal-title-line-height:     @line-height-base;
+
+//** Background color of modal content area
+@modal-content-bg:                             #fff;
+//** Modal content border color
+@modal-content-border-color:                   rgba(0,0,0,.2);
+//** Modal content border color **for IE8**
+@modal-content-fallback-border-color:          #999;
+
+//** Modal backdrop background color
+@modal-backdrop-bg:           #000;
+//** Modal backdrop opacity
+@modal-backdrop-opacity:      .5;
+//** Modal header border color
+@modal-header-border-color:   #e5e5e5;
+//** Modal footer border color
+@modal-footer-border-color:   @modal-header-border-color;
+
+@modal-lg:                    900px;
+@modal-md:                    600px;
+@modal-sm:                    300px;
+
+
+//== Alerts
+//
+//## Define alert colors, border radius, and padding.
+
+@alert-padding:               15px;
+@alert-border-radius:         @border-radius-base;
+@alert-link-font-weight:      bold;
+
+@alert-success-bg:            @state-success-bg;
+@alert-success-text:          @state-success-text;
+@alert-success-border:        @state-success-border;
+
+@alert-info-bg:               @state-info-bg;
+@alert-info-text:             @state-info-text;
+@alert-info-border:           @state-info-border;
+
+@alert-warning-bg:            @state-warning-bg;
+@alert-warning-text:          @state-warning-text;
+@alert-warning-border:        @state-warning-border;
+
+@alert-danger-bg:             @state-danger-bg;
+@alert-danger-text:           @state-danger-text;
+@alert-danger-border:         @state-danger-border;
+
+
+//== Progress bars
+//
+//##
+
+//** Background color of the whole progress component
+@progress-bg:                 #f5f5f5;
+//** Progress bar text color
+@progress-bar-color:          #fff;
+//** Variable for setting rounded corners on progress bar.
+@progress-border-radius:      @border-radius-base;
+
+//** Default progress bar color
+@progress-bar-bg:             @brand-primary;
+//** Success progress bar color
+@progress-bar-success-bg:     @brand-success;
+//** Warning progress bar color
+@progress-bar-warning-bg:     @brand-warning;
+//** Danger progress bar color
+@progress-bar-danger-bg:      @brand-danger;
+//** Info progress bar color
+@progress-bar-info-bg:        @brand-info;
+
+
+//== List group
+//
+//##
+
+//** Background color on `.list-group-item`
+@list-group-bg:                 #fff;
+//** `.list-group-item` border color
+@list-group-border:             #ddd;
+//** List group border radius
+@list-group-border-radius:      @border-radius-base;
+
+//** Background color of single list items on hover
+@list-group-hover-bg:           #f5f5f5;
+//** Text color of active list items
+@list-group-active-color:       @component-active-color;
+//** Background color of active list items
+@list-group-active-bg:          @component-active-bg;
+//** Border color of active list elements
+@list-group-active-border:      @list-group-active-bg;
+//** Text color for content within active list items
+@list-group-active-text-color:  lighten(@list-group-active-bg, 40%);
+
+//** Text color of disabled list items
+@list-group-disabled-color:      @gray-light;
+//** Background color of disabled list items
+@list-group-disabled-bg:         @gray-lighter;
+//** Text color for content within disabled list items
+@list-group-disabled-text-color: @list-group-disabled-color;
+
+@list-group-link-color:         #555;
+@list-group-link-hover-color:   @list-group-link-color;
+@list-group-link-heading-color: #333;
+
+
+//== Panels
+//
+//##
+
+@panel-bg:                    #fff;
+@panel-body-padding:          15px;
+@panel-heading-padding:       10px 15px;
+@panel-footer-padding:        @panel-heading-padding;
+@panel-border-radius:         @border-radius-base;
+
+//** Border color for elements within panels
+@panel-inner-border:          #ddd;
+@panel-footer-bg:             #f5f5f5;
+
+@panel-default-text:          @gray-dark;
+@panel-default-border:        #ddd;
+@panel-default-heading-bg:    #f5f5f5;
+
+@panel-primary-text:          #fff;
+@panel-primary-border:        @brand-primary;
+@panel-primary-heading-bg:    @brand-primary;
+
+@panel-success-text:          @state-success-text;
+@panel-success-border:        @state-success-border;
+@panel-success-heading-bg:    @state-success-bg;
+
+@panel-info-text:             @state-info-text;
+@panel-info-border:           @state-info-border;
+@panel-info-heading-bg:       @state-info-bg;
+
+@panel-warning-text:          @state-warning-text;
+@panel-warning-border:        @state-warning-border;
+@panel-warning-heading-bg:    @state-warning-bg;
+
+@panel-danger-text:           @state-danger-text;
+@panel-danger-border:         @state-danger-border;
+@panel-danger-heading-bg:     @state-danger-bg;
+
+
+//== Thumbnails
+//
+//##
+
+//** Padding around the thumbnail image
+@thumbnail-padding:           4px;
+//** Thumbnail background color
+@thumbnail-bg:                @body-bg;
+//** Thumbnail border color
+@thumbnail-border:            #ddd;
+//** Thumbnail border radius
+@thumbnail-border-radius:     @border-radius-base;
+
+//** Custom text color for thumbnail captions
+@thumbnail-caption-color:     @text-color;
+//** Padding around the thumbnail caption
+@thumbnail-caption-padding:   9px;
+
+
+//== Wells
+//
+//##
+
+@well-bg:                     #f5f5f5;
+@well-border:                 darken(@well-bg, 7%);
+
+
+//== Badges
+//
+//##
+
+@badge-color:                 #fff;
+//** Linked badge text color on hover
+@badge-link-hover-color:      #fff;
+@badge-bg:                    @gray-light;
+
+//** Badge text color in active nav link
+@badge-active-color:          @link-color;
+//** Badge background color in active nav link
+@badge-active-bg:             #fff;
+
+@badge-font-weight:           bold;
+@badge-line-height:           1;
+@badge-border-radius:         10px;
+
+
+//== Breadcrumbs
+//
+//##
+
+@breadcrumb-padding-vertical:   8px;
+@breadcrumb-padding-horizontal: 15px;
+//** Breadcrumb background color
+@breadcrumb-bg:                 #f5f5f5;
+//** Breadcrumb text color
+@breadcrumb-color:              #ccc;
+//** Text color of current page in the breadcrumb
+@breadcrumb-active-color:       @gray-light;
+//** Textual separator for between breadcrumb elements
+@breadcrumb-separator:          "/";
+
+
+//== Carousel
+//
+//##
+
+@carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6);
+
+@carousel-control-color:                      #fff;
+@carousel-control-width:                      15%;
+@carousel-control-opacity:                    .5;
+@carousel-control-font-size:                  20px;
+
+@carousel-indicator-active-bg:                #fff;
+@carousel-indicator-border-color:             #fff;
+
+@carousel-caption-color:                      #fff;
+
+
+//== Close
+//
+//##
+
+@close-font-weight:           bold;
+@close-color:                 #000;
+@close-text-shadow:           0 1px 0 #fff;
+
+
+//== Code
+//
+//##
+
+@code-color:                  #c7254e;
+@code-bg:                     #f9f2f4;
+
+@kbd-color:                   #fff;
+@kbd-bg:                      #333;
+
+@pre-bg:                      #f5f5f5;
+@pre-color:                   @gray-dark;
+@pre-border-color:            #ccc;
+@pre-scrollable-max-height:   340px;
+
+
+//== Type
+//
+//##
+
+//** Horizontal offset for forms and lists.
+@component-offset-horizontal: 180px;
+//** Text muted color
+@text-muted:                  @gray-light;
+//** Abbreviations and acronyms border color
+@abbr-border-color:           @gray-light;
+//** Headings small color
+@headings-small-color:        @gray-light;
+//** Blockquote small color
+@blockquote-small-color:      @gray-light;
+//** Blockquote font size
+@blockquote-font-size:        (@font-size-base * 1.25);
+//** Blockquote border color
+@blockquote-border-color:     @gray-lighter;
+//** Page header border color
+@page-header-border-color:    @gray-lighter;
+//** Width of horizontal description list titles
+@dl-horizontal-offset:        @component-offset-horizontal;
+//** Horizontal line color.
+@hr-border:                   #b2b2b2;
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/buttons.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/buttons.less
new file mode 100644
index 00000000..bbb3c620
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/buttons.less
@@ -0,0 +1,61 @@
+//
+// Buttons
+// --------------------------------------------------
+
+
+// Base styles
+// --------------------------------------------------
+
+// Core styles
+.btn {
+  display: inline-block;
+  padding: 5px 6px 4px 8px;
+  margin-top: 0;
+  margin-bottom: 0; // For input.btn
+  font-family: "Open Sans Condensed", sans-serif;
+  font-size: @font-size-base * 1.3;
+  font-weight: 700;
+  text-transform: uppercase;
+  letter-spacing: 1px;
+  line-height: @line-height-computed;
+  text-align: center;
+  vertical-align: middle;
+  touch-action: manipulation;
+  cursor: pointer;
+  background-color: @gray-lighter;
+  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214
+  border: none;
+  border-radius: 3px;
+  .box-shadow(1px 1px 0 rgba(0, 0, 0, 0.3));
+  outline: none !important;
+  white-space: nowrap;
+  .user-select(none);
+
+  &:focus,
+  &.focus {
+    .tab-focus();
+  }
+
+  &:hover,
+  &:focus,
+  &.focus {
+    background-color: lighten(@gray-lighter, 5%);
+    text-decoration: none;
+    .transition(background-color .1s linear);
+  }
+
+  &:active,
+  &.active {
+    outline: 0;
+    padding: 6px 5px 3px 9px;
+  }
+
+  &.disabled,
+  &[disabled],
+  fieldset[disabled] & {
+    cursor: @cursor-disabled;
+    pointer-events: none; // Future-proof disabling of clicks
+    .opacity(65);
+    .box-shadow(1px 1px 0 rgba(0, 0, 0, 0.15));
+  }
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/code.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/code.less
new file mode 100644
index 00000000..33af7555
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/code.less
@@ -0,0 +1,8 @@
+code {
+  margin: 0 2px;
+  padding: 1px 5px;
+  border: 1px solid #E1E1E8;
+  background-color: #F7F7F9;
+  color: @gray-dark;
+  font-size: 85%;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/evaluation-form.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/evaluation-form.less
new file mode 100644
index 00000000..6d505d56
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/evaluation-form.less
@@ -0,0 +1,91 @@
+//
+// Evaluation form
+// --------------------------------------------------
+
+
+.evaluation-form {
+  .clearfix();
+
+  textarea {
+    height: 200px;
+  }
+}
+
+.evaluation-input-output {
+  float: left;
+  width: 65%;
+}
+
+.evaluation-input-clear-button {
+  position: absolute;
+  top: 0;
+  right: 0;
+  width: 15px;
+  height: 15px;
+  margin: 6px;
+  display: inline-block;
+  background: transparent url("../images/clear-text.png") no-repeat scroll 0 0;
+  opacity: 0.75;
+  line-height: 0;
+  vertical-align: bottom;
+  cursor: pointer;
+
+  &:hover {
+   opacity: 1;
+ }
+}
+
+.evaluation-input-clear-button.with-scrollbar {
+  margin-right: 29px;
+}
+
+.evaluation-output {
+  margin-top: 1.5em;
+}
+
+.evaluation-input-field,
+.evaluation-output-field {
+  margin-bottom: 0.5em;
+  .box-sizing(border-box);
+  overflow-x: hidden;
+  line-height: @line-height-computed;
+}
+
+.evaluation-output-field {
+  &[readonly] {
+    cursor: auto;
+    background-color: @input-bg;
+  }
+}
+
+.evaluation-error-header {
+  margin-bottom: @line-height-computed;
+  padding-left: 52px;
+  background: transparent url("../images/icons/48x48/error.png") no-repeat scroll 0 50%;
+  color: @black;
+  font-family: @font-family-sans-serif;
+  font-weight: bold;
+  line-height: 48px;
+  vertical-align: middle;
+}
+
+ul.evaluation-error-list,
+ul.evaluation-warning-list {
+  li {
+    margin-bottom: 1em;
+
+    pre {
+      margin-top: 0.5em;
+      white-space: pre;
+      overflow: auto;
+    }
+  }
+}
+
+ul.evaluation-error-list li pre {
+  background-color: @state-danger-bg;
+}
+
+ul.evaluation-warning-list li pre {
+  background-color: @state-warning-bg;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/forms.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/forms.less
new file mode 100644
index 00000000..2ceb0559
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/forms.less
@@ -0,0 +1,99 @@
+//
+// Forms
+// --------------------------------------------------
+
+// GENERAL STYLES
+// --------------
+
+label {
+  font-weight: normal;
+}
+
+
+// Form controls
+// -------------------------
+
+// Common form controls
+.form-control {
+  line-height: @input-height-base;
+}
+
+.textarea-wrapper {
+  position: relative;
+}
+
+// Form groups
+.form-group {
+  margin-bottom: 0.5em;
+}
+
+// Info and errors
+// --------------------------
+.message-info {
+  border: 1px solid;
+  clear: both;
+  padding: 10px 20px;
+  color: @state-info-text;
+}
+
+.message-error {
+  clear: both;
+  color: @state-danger-text;
+  font-size: 1.1em;
+  font-weight: bold;
+  margin: 20px 0 10px 0;
+}
+
+.message-success {
+  color: @state-success-text;
+  font-size: 1.3em;
+  font-weight: bold;
+  margin: 20px 0 10px 0;
+}
+
+.error {
+  color: @state-danger-text;
+}
+
+// Styles for validation helpers
+// --------------------------
+.validators {
+  font-style: normal;
+  padding-bottom: 0.5em;
+}
+
+.field-validation-error {
+  display: block;
+  padding-bottom: 0.5em;
+  color: @state-danger-text;
+  font-weight: bold;
+}
+
+.field-validation-valid {
+  display: none;
+}
+
+input.input-validation-error,
+textarea.input-validation-error,
+select.input-validation-error
+{
+  border: 1px solid @state-danger-border;
+  background-color: @state-danger-bg;
+  .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
+
+  &:focus {
+    border-color: @state-danger-border;
+    @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@state-danger-text, 20%);
+    .box-shadow(@shadow);
+  }
+}
+
+.validation-summary-errors {
+  color: @state-danger-text;
+  font-weight: bold;
+  font-size: 1.1em;
+}
+
+.validation-summary-valid {
+  display: none;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/icons.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/icons.less
new file mode 100644
index 00000000..4a80f963
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/icons.less
@@ -0,0 +1,36 @@
+//
+// Icons
+// --------------------------------------------------
+
+
+.icon {
+  background: transparent url(../images/icons/32x32/social-media-icons-32.png) no-repeat scroll;
+  display: inline-block;
+  line-height: 0;
+  vertical-align: bottom;
+}
+
+.icon-32 {
+  height: 32px;
+  width: 32px;
+}
+
+.icon-mastodon {
+  background-position: 0 0;
+}
+
+.icon-x {
+  background-position: 0 -32px;
+}
+
+.icon-linkedin {
+  background-position: 0 -64px;
+}
+
+.icon-bluesky {
+  background-position: 0 -96px;
+}
+
+.icon-rss {
+  background-position: 0 -128px;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/layout.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/layout.less
new file mode 100644
index 00000000..35a7f386
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/layout.less
@@ -0,0 +1,128 @@
+html,
+body {
+  margin: 0;
+  height: 100%;
+  /* The html and body elements cannot have any padding or margin */
+}
+
+/* Wrapper for page content to push down footer */
+.l-wrapper {
+  min-height: 100%;
+  height: auto !important;
+  height: 100%;
+  /* Negative indent footer by it's height */
+  margin: 0 auto (-1 * @footer-height);
+}
+
+.l-constrained {
+  margin: 0 auto;
+  padding-left: 20px;
+  padding-right: 20px;
+  max-width: 1200px;
+  min-width: 800px;
+}
+
+.l-header {
+  border-top: solid 10px @black;
+  border-bottom: 1px solid #d4b33a;
+  background-color: #f0db4f;
+
+  .l-constrained {
+    padding-top: 20px;
+    padding-bottom: 20px;
+    .clearfix();
+  }
+}
+
+.l-content {
+  .clearfix();
+}
+
+.l-main-content {
+  float: left;
+  width: 70%;
+}
+
+.l-sidebar {
+  float: right;
+  width: 30%;
+}
+
+/* Set the fixed height of the footer here */
+.l-push,
+.l-footer {
+  height: @footer-height;
+}
+
+.l-footer {
+  width: 100%;
+  background-color: @black;
+  color: @gray-lighter;
+  font-size: @font-size-small;
+  line-height: 100%;
+
+  .l-constrained {
+    padding-top: (@footer-height - 32) / 2;
+    .clearfix();
+  }
+
+  a {
+    color: @gray-lighter;
+    text-decoration: none;
+
+    &:hover,
+    &:active {
+      text-decoration: underline;
+    }
+  }
+}
+
+
+// Header
+// -------------------------
+
+.logo {
+  margin: 0;
+
+  a {
+    display: block;
+    float: left;
+    width: 100px;
+    height: 100px;
+    background: transparent url("../images/jsengineswitcher-logo.png") no-repeat scroll 0 0;
+    text-indent: -9999em;
+  }
+}
+
+.primary-nav {
+  position: relative;
+  left: 40px;
+  float: left;
+}
+
+
+// Footer
+// -------------------------
+
+.l-copyright {
+  float: left;
+    p {
+      margin: 0;
+      padding: 0;
+  }
+}
+
+.l-social {
+  float: right;
+
+  li {
+    display: inline;
+    list-style: none outside none;
+    padding-left: 10px;
+
+    a {
+      text-indent: -9999px;
+      outline: none;
+    }
+  }
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/mixins.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/mixins.less
new file mode 100644
index 00000000..d26eee79
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/mixins.less
@@ -0,0 +1,9 @@
+//
+// Mixins
+// --------------------------------------------------
+
+// NO Drop shadows
+.no-box-shadow {
+  -webkit-box-shadow: none; // iOS <4.3 & Android <4.1
+          box-shadow: none;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/navbar.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/navbar.less
new file mode 100644
index 00000000..95edf3c2
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/navbar.less
@@ -0,0 +1,38 @@
+//
+// Navbars
+// --------------------------------------------------
+
+
+// Wrapper and base class
+//
+// Provide a static navbar from which we expand to create full-width, fixed, and
+// other navbar variations.
+
+.navbar {
+  border: none;
+  border-radius: 0;
+}
+
+// Navbar nav links
+//
+// Builds on top of the `.nav` components with it's own modifier class to make
+// the nav the full height of the horizontal nav (above 768px).
+
+.navbar-nav {
+  float: left;
+  margin: 0 auto;
+
+  > li {
+    > a {
+      float: none;
+      // Vertically center the text given @navbar-height
+      padding: ((@navbar-height - @line-height-computed) / 2) 15px ((@navbar-height - @line-height-computed) / 2);
+      border: 1px solid transparent;
+      font-family: "Open Sans", sans-serif;
+      font-size: 2.0em;
+      text-decoration: none;
+      text-transform: uppercase;
+      outline: none;
+    }
+  }
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/page-socials.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/page-socials.less
new file mode 100644
index 00000000..ea12dc09
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/page-socials.less
@@ -0,0 +1,14 @@
+.page-socials {
+  min-height: 60px;
+  margin-top: 20px;
+  padding-top: 15px;
+  border-top: 1px solid #b2b2b2;
+}
+
+.share-buttons {
+  li {
+    margin-right: 15px;
+    vertical-align: top;
+    line-height: normal;
+  }
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/type.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/type.less
new file mode 100644
index 00000000..02d69502
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/type.less
@@ -0,0 +1,24 @@
+//
+// Typography
+// --------------------------------------------------
+
+// Headings
+// -------------------------
+
+h1, h2, h3 {
+  margin-top: 10px;
+}
+
+h1, h2 {
+  line-height: 40px;
+}
+
+h2 {
+  color: @black;
+  font-weight: bold;
+}
+
+h3 {
+  color: @gray-dark;
+  line-height: @line-height-computed * 1.25;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/variables.less b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/variables.less
new file mode 100644
index 00000000..498c9796
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNet4.Mvc4/styles/variables.less
@@ -0,0 +1,7 @@
+// Colors
+// -------------------------
+@black:                 #1d0d0d;
+
+// Footer
+// -------------------------
+@footer-height:                    55px;
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/.bowerrc b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/.bowerrc
new file mode 100644
index 00000000..ea81a597
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/.bowerrc
@@ -0,0 +1,4 @@
+{
+  "registry": "https://registry.bower.io",
+  "directory": "wwwroot/lib"
+}
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets.csproj
new file mode 100644
index 00000000..ae4c7d82
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets.csproj
@@ -0,0 +1,11 @@
+
+
+  
+    JS Engine Switcher: Client-Side Assets for ASP.NET Core Samples
+    3.30.2
+    netstandard2.0
+    Library
+    false
+  
+
+
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/bower.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/bower.json
new file mode 100644
index 00000000..3a9323a2
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/bower.json
@@ -0,0 +1,11 @@
+{
+  "name": "javascriptengineswitcher.sample.aspnetcore.clientsideassets",
+  "dependencies": {
+    "modernizr": "2.8.3",
+    "jquery-compat": "jquery#1.10.2",
+    "jquery": "jquery#2.0.3",
+    "jquery-validation": "1.13.1",
+    "jquery-validation-unobtrusive": "3.2.3",
+    "bootstrap": "3.3.0"
+  }
+}
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/build-frontend.cmd b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/build-frontend.cmd
new file mode 100644
index 00000000..de3af278
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/build-frontend.cmd
@@ -0,0 +1,41 @@
+@echo off
+setlocal
+
+::--------------------------------------------------------------------------------
+:: Build
+::--------------------------------------------------------------------------------
+
+echo Starting to build the frontend for ASP.NET Core samples ...
+echo.
+
+echo Installing Node.js packages ...
+echo.
+call npm install
+if errorlevel 1 goto error
+echo.
+
+echo Installing Bower packages ...
+echo.
+call bower install
+if errorlevel 1 goto error
+echo.
+
+echo Building client-side assets ...
+echo.
+call gulp
+if errorlevel 1 goto error
+echo.
+
+::--------------------------------------------------------------------------------
+:: Exit
+::--------------------------------------------------------------------------------
+
+echo Succeeded!
+goto exit
+
+:error
+echo *** Error: The previous step failed!
+
+:exit
+cd ../../
+endlocal
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/build-frontend.sh b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/build-frontend.sh
new file mode 100644
index 00000000..06a7afd9
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/build-frontend.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+
+function handle_error()
+{
+    _RET=${PIPESTATUS[0]}
+    if [[ $_RET != 0 ]]; then
+        echo "*** Error: The previous step failed!"
+
+        cd ../../
+        exit $_RET
+    fi
+}
+
+echo "Starting to build the frontend for ASP.NET Core samples ..."
+echo ""
+
+echo "Installing Node.js packages ..."
+echo ""
+npm install
+handle_error
+echo ""
+
+echo "Installing Bower packages ..."
+echo ""
+bower install
+handle_error
+echo ""
+
+echo "Building client-side assets ..."
+echo ""
+gulp
+handle_error
+echo ""
+
+echo "Succeeded!"
+
+cd ../../
+exit $_RET
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/ensure-client-side-assets-builded.targets b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/ensure-client-side-assets-builded.targets
new file mode 100644
index 00000000..3f41ba35
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/ensure-client-side-assets-builded.targets
@@ -0,0 +1,8 @@
+
+
+  
+    
+  
+
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/gulpfile.js b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/gulpfile.js
new file mode 100644
index 00000000..0f259437
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/gulpfile.js
@@ -0,0 +1,130 @@
+/*global require, exports */
+/*jshint esversion: 6 */
+const WEB_ROOT_PATH = "wwwroot";
+const BOWER_DIR_PATH = WEB_ROOT_PATH + "/lib";
+const STYLE_DIR_PATH = WEB_ROOT_PATH + '/styles';
+const SCRIPT_DIR_PATH = WEB_ROOT_PATH + '/scripts';
+
+// include plug-ins
+let { src, dest, series, parallel, watch } = require('gulp');
+let del = require('del');
+let sourcemaps = require('gulp-sourcemaps');
+let rename = require('gulp-rename');
+let concat = require('gulp-concat');
+let less = require('gulp-less');
+let autoprefixer = require('gulp-autoprefixer');
+let cleanCss = require('gulp-clean-css');
+let uglify = require('gulp-uglify');
+
+//#region Clean
+//#region Clean builded assets
+function cleanBuildedStyles() {
+	return del([STYLE_DIR_PATH + '/build/*']);
+}
+
+function cleanBuildedScripts() {
+	return del([SCRIPT_DIR_PATH + '/build/*']);
+}
+
+let cleanBuildedAssets = parallel(cleanBuildedStyles, cleanBuildedScripts);
+//#endregion
+//#endregion
+
+//#region Build assets
+//#region Build styles
+let autoprefixerOptions = {
+	overrideBrowserslist: ['> 1%', 'last 3 versions', 'Firefox ESR', 'Opera 12.1'],
+	cascade: true
+};
+let cssCleanOptions = { specialComments: '*' };
+let cssRenameOptions = { extname: '.min.css' };
+
+function buildCommonStyles() {
+	return src([STYLE_DIR_PATH + '/app.less'])
+		.pipe(sourcemaps.init())
+		.pipe(less({
+			relativeUrls: true,
+			rootpath: '/styles/'
+		}))
+		.pipe(autoprefixer(autoprefixerOptions))
+		.pipe(sourcemaps.write('./'))
+		.pipe(dest(STYLE_DIR_PATH + '/build'))
+		.pipe(sourcemaps.init({ loadMaps: true }))
+		.pipe(concat('common-styles.css'))
+		.pipe(cleanCss(cssCleanOptions))
+		.pipe(rename(cssRenameOptions))
+		.pipe(sourcemaps.write('./'))
+		.pipe(dest(STYLE_DIR_PATH + '/build'))
+		;
+}
+
+let buildStyles = buildCommonStyles;
+//#endregion
+
+//#region Build scripts
+let jsConcatOptions = { newLine: ';' };
+let jsUglifyOptions = {
+	output: { comments: /^!/ }
+};
+let jsRenameOptions = { extname: '.min.js' };
+
+function buildModernizrScripts() {
+	return src([BOWER_DIR_PATH + '/modernizr/modernizr.js'])
+		.pipe(sourcemaps.init())
+		.pipe(uglify(jsUglifyOptions))
+		.pipe(rename(jsRenameOptions))
+		.pipe(sourcemaps.write('./'))
+		.pipe(dest(SCRIPT_DIR_PATH + '/build'))
+		;
+}
+
+function buildCommonScripts() {
+	return src([SCRIPT_DIR_PATH + '/common.js'])
+		.pipe(sourcemaps.init({ loadMaps: true }))
+		.pipe(rename({ basename: 'common-scripts' }))
+		.pipe(uglify(jsUglifyOptions))
+		.pipe(rename(jsRenameOptions))
+		.pipe(sourcemaps.write('./'))
+		.pipe(dest(SCRIPT_DIR_PATH + '/build'))
+		;
+}
+
+function buildEvaluationFormScripts() {
+	return src([BOWER_DIR_PATH + '/jquery-validation/dist/jquery.validate.js',
+			BOWER_DIR_PATH + '/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js',
+			BOWER_DIR_PATH + '/bootstrap/js/button.js',
+			SCRIPT_DIR_PATH + '/evaluation-form.js'])
+		.pipe(sourcemaps.init({ loadMaps: true }))
+		.pipe(concat('evaluation-form-scripts.js', jsConcatOptions))
+		.pipe(uglify(jsUglifyOptions))
+		.pipe(rename(jsRenameOptions))
+		.pipe(sourcemaps.write('./'))
+		.pipe(dest(SCRIPT_DIR_PATH + '/build'))
+		;
+}
+
+let buildScripts = parallel(buildModernizrScripts, buildCommonScripts, buildEvaluationFormScripts);
+//#endregion
+
+let buildAssets = parallel(buildStyles, buildScripts);
+//#endregion
+
+//#region Watch assets
+function watchStyles() {
+	return watch([STYLE_DIR_PATH + '/**/*.{less,css}', '!' + STYLE_DIR_PATH + '/build/**/*.*'],
+		buildStyles);
+}
+
+function watchScripts() {
+	return watch([SCRIPT_DIR_PATH + '/**/*.js', '!' + SCRIPT_DIR_PATH + '/build/**/*.*'],
+		buildScripts);
+}
+
+let watchAssets = parallel(watchStyles, watchScripts);
+//#endregion
+
+// Export tasks
+exports.cleanBuildedAssets = cleanBuildedAssets;
+exports.buildAssets = buildAssets;
+exports.watchAssets = watchAssets;
+exports.default = series(cleanBuildedAssets, buildAssets);
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/package.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/package.json
new file mode 100644
index 00000000..725f9bac
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/package.json
@@ -0,0 +1,16 @@
+{
+  "name": "javascriptengineswitcher.sample.aspnetcore.clientsideassets",
+  "private": true,
+  "version": "3.30.2",
+  "devDependencies": {
+    "gulp": "4.0.2",
+    "del": "5.1.0",
+    "gulp-sourcemaps": "2.6.5",
+    "gulp-rename": "2.0.0",
+    "gulp-concat": "2.6.1",
+    "gulp-less": "4.0.1",
+    "gulp-autoprefixer": "7.0.1",
+    "gulp-clean-css": "4.2.0",
+    "gulp-uglify": "3.0.2"
+  }
+}
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/0.gif b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/0.gif
new file mode 100644
index 00000000..35d42e80
Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/0.gif differ
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/clear-text.png b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/clear-text.png
new file mode 100644
index 00000000..9d217b7f
Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/clear-text.png differ
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/icons/32x32/social-media-icons-32.png b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/icons/32x32/social-media-icons-32.png
new file mode 100644
index 00000000..30d1c781
Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/icons/32x32/social-media-icons-32.png differ
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/icons/48x48/error.png b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/icons/48x48/error.png
new file mode 100644
index 00000000..3d6185f7
Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/icons/48x48/error.png differ
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/jsengineswitcher-logo.png b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/jsengineswitcher-logo.png
new file mode 100644
index 00000000..12ed97e0
Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/images/jsengineswitcher-logo.png differ
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/scripts/_references.js b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/scripts/_references.js
new file mode 100644
index 00000000..2b54eea8
Binary files /dev/null and b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/scripts/_references.js differ
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/scripts/common.js b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/scripts/common.js
new file mode 100644
index 00000000..c94bf102
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/scripts/common.js
@@ -0,0 +1,29 @@
+var jsEngineSwitcher;
+
+(function (jsEngineSwitcher, undefined) {
+	"use strict";
+
+	jsEngineSwitcher.registerNamespace = function (namespaceString) {
+		var parts = namespaceString.split("."),
+			parent = jsEngineSwitcher,
+			i
+			;
+
+		if (parts[0] === "jsEngineSwitcher") {
+			parts = parts.slice(1);
+		}
+
+		for (i = 0; i < parts.length; i += 1) {
+			if (typeof parent[parts[i]] === "undefined") {
+				parent[parts[i]] = {};
+			}
+			parent = parent[parts[i]];
+		}
+
+		return parent;
+	};
+
+	jsEngineSwitcher.hasScrollbar = function(elem) {
+		return (elem.clientHeight < elem.scrollHeight);
+	};
+}(jsEngineSwitcher = jsEngineSwitcher || {}));
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/scripts/evaluation-form.js b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/scripts/evaluation-form.js
new file mode 100644
index 00000000..da1f0199
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/scripts/evaluation-form.js
@@ -0,0 +1,83 @@
+(function (jsEngineSwitcher, $, undefined) {
+	"use strict";
+
+	var $evaluationForm,
+		$evaluationInputField,
+		$evaluationInputClearButton,
+		$evaluateButton
+		;
+
+	$(function () {
+		$evaluationForm = $("form[data-form-type='evaluation-form']");
+		$evaluationInputField = $(":input[data-control-type='evaluation-input-field']", $evaluationForm);
+		$evaluationInputClearButton = $("
"); + $evaluateButton = $(":input[data-control-type='evaluate-button']", $evaluationForm); + + $evaluationForm.on("submit", onEvaluationFormSubmitHandler); + + $evaluationInputClearButton.on("click", onEvaluationInputClearButtonClickHandler); + $evaluationInputField.parent().append($evaluationInputClearButton); + refreshEvaluationInputClearButton(); + $evaluationInputField + .on("input propertychange keydown keyup paste", onEvaluationInputFieldChangeHandler) + ; + + $evaluateButton.removeAttr("disabled"); + }); + + $(window).unload(function() { + $evaluationForm.off("submit", onEvaluationFormSubmitHandler); + + $evaluationInputClearButton + .off("click", onEvaluationInputClearButtonClickHandler) + .remove() + ; + + $evaluationInputField + .off("input propertychange keydown keyup paste", onEvaluationInputFieldChangeHandler) + ; + + $evaluationForm = null; + $evaluationInputField = null; + $evaluationInputClearButton = null; + $evaluateButton = null; + }); + + var refreshEvaluationInputClearButton = function() { + if ($.trim($evaluationInputField.val()).length > 0) { + $evaluationInputClearButton.show(); + } else { + $evaluationInputClearButton.hide(); + } + + if (jsEngineSwitcher.hasScrollbar($evaluationInputField.get(0))) { + $evaluationInputClearButton.addClass("with-scrollbar"); + } + else { + $evaluationInputClearButton.removeClass("with-scrollbar"); + } + }; + + var onEvaluationFormSubmitHandler = function () { + var $form = $(this); + if ($form.valid()) { + $evaluateButton.attr("disabled", "disabled"); + $("textarea[data-control-type='evaluation-output-field']", $form).val(''); + + return true; + } + + return false; + }; + + var onEvaluationInputFieldChangeHandler = function () { + refreshEvaluationInputClearButton(); + }; + + var onEvaluationInputClearButtonClickHandler = function() { + $evaluationInputField.val(""); + + var $button = $(this); + $button.hide(); + }; +}(jsEngineSwitcher, jQuery)); \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/app.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/app.less new file mode 100644 index 00000000..c41a9315 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/app.less @@ -0,0 +1,71 @@ +/*! + * Bootstrap v3.3.0 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +// Core variables and mixins +@import "bootstrap-custom-variables.less"; +@import "../lib/bootstrap/less/mixins.less"; + +// Reset +@import "../lib/bootstrap/less/normalize.less"; +//@import "../lib/bootstrap/less/print.less"; +//@import "../lib/bootstrap/less/glyphicons.less"; + +// Core CSS +@import "../lib/bootstrap/less/scaffolding.less"; +@import "../lib/bootstrap/less/type.less"; +@import "../lib/bootstrap/less/code.less"; +//@import "../lib/bootstrap/less/grid.less"; +//@import "../lib/bootstrap/less/tables.less"; +@import "../lib/bootstrap/less/forms.less"; +//@import "../lib/bootstrap/less/buttons.less"; + +// Components +//@import "../lib/bootstrap/less/component-animations.less"; +//@import "../lib/bootstrap/less/dropdowns.less"; +//@import "../lib/bootstrap/less/button-groups.less"; +//@import "../lib/bootstrap/less/input-groups.less"; +@import "../lib/bootstrap/less/navs.less"; +@import "../lib/bootstrap/less/navbar.less"; +//@import "../lib/bootstrap/less/breadcrumbs.less"; +//@import "../lib/bootstrap/less/pagination.less"; +//@import "../lib/bootstrap/less/pager.less"; +//@import "../lib/bootstrap/less/labels.less"; +//@import "../lib/bootstrap/less/badges.less"; +//@import "../lib/bootstrap/less/jumbotron.less"; +//@import "../lib/bootstrap/less/thumbnails.less"; +//@import "../lib/bootstrap/less/alerts.less"; +//@import "../lib/bootstrap/less/progress-bars.less"; +//@import "../lib/bootstrap/less/media.less"; +//@import "../lib/bootstrap/less/list-group.less"; +//@import "../lib/bootstrap/less/panels.less"; +//@import "../lib/bootstrap/less/responsive-embed.less"; +//@import "../lib/bootstrap/less/wells.less"; +//@import "../lib/bootstrap/less/close.less"; + +// Components w/ JavaScript +//@import "../lib/bootstrap/less/modals.less"; +//@import "../lib/bootstrap/less/tooltip.less"; +//@import "../lib/bootstrap/less/popovers.less"; +//@import "../lib/bootstrap/less/carousel.less"; + +// Utility classes +@import "../lib/bootstrap/less/utilities.less"; +//@import "../lib/bootstrap/less/responsive-utilities.less"; + +// JavaScriptEngineSwitcher specs +@import "variables.less"; +@import "mixins.less"; + +@import "type.less"; +@import "code.less"; +@import "forms.less"; +@import "buttons.less"; +@import "icons.less"; +@import "navbar.less"; + +@import "layout.less"; +@import "evaluation-form.less"; +@import "page-socials.less"; \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/bootstrap-custom-variables.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/bootstrap-custom-variables.less new file mode 100644 index 00000000..0be2aa6f --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/bootstrap-custom-variables.less @@ -0,0 +1,856 @@ +// +// Variables +// -------------------------------------------------- + + +//== Colors +// +//## Gray and brand colors for use across Bootstrap. + +@gray-base: #323330; +@gray-darker: lighten(@gray-base, 13.5%); // #222 +@gray-dark: lighten(@gray-base, 20%); // #333 +@gray: lighten(@gray-base, 33.5%); // #555 +@gray-light: lighten(@gray-base, 46.7%); // #777 +@gray-lighter: #e3e3e3; + +@brand-primary: #428bca; +@brand-success: #5cb85c; +@brand-info: #5bc0de; +@brand-warning: #f0ad4e; +@brand-danger: #d9534f; + + +//== Scaffolding +// +//## Settings for some of the most global styles. + +//** Background color for ``. +@body-bg: #fff; +//** Global text color on ``. +@text-color: #1d0d0d; + +//** Global textual link color. +@link-color: @brand-primary; +//** Link hover color set via `darken()` function. +@link-hover-color: darken(@link-color, 15%); +//** Link hover decoration. +@link-hover-decoration: underline; + + +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. + +@font-family-sans-serif: "Helvetica Neue", Arial, sans-serif; +@font-family-serif: Georgia, "Times New Roman", Times, serif; +//** Default monospace fonts for ``, ``, and `
`.
+@font-family-monospace:   Consolas, "Courier New", monospace;
+@font-family-base:        @font-family-sans-serif;
+
+@font-size-base:          14px;
+@font-size-large:         ceil((@font-size-base * 1.25)); // ~18px
+@font-size-small:         ceil((@font-size-base * 0.85)); // ~12px
+
+@font-size-h1:            floor(@font-size-base * 3);
+@font-size-h2:            @font-size-base * 2.5;
+@font-size-h3:            ceil(@font-size-base * 1.75);
+@font-size-h4:            ceil(@font-size-base * 1.25);
+@font-size-h5:            @font-size-base;
+@font-size-h6:            ceil(@font-size-base * 0.85);
+
+//** Unit-less `line-height` for use in components like buttons.
+@line-height-base:        1.428571429; // 20/14
+//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
+@line-height-computed:    floor((@font-size-base * @line-height-base)); // ~20px
+
+//** By default, this inherits from the ``.
+@headings-font-family:    "Open Sans", sans-serif;
+@headings-font-weight:    normal;
+@headings-line-height:    1.1;
+@headings-color:          inherit;
+
+
+//== Iconography
+//
+//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
+
+//** Load fonts from this directory.
+@icon-font-path:          "../fonts/";
+//** File name for all font files.
+@icon-font-name:          "glyphicons-halflings-regular";
+//** Element ID within SVG icon file.
+@icon-font-svg-id:        "glyphicons_halflingsregular";
+
+
+//== Components
+//
+//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
+
+@padding-base-vertical:     4px;
+@padding-base-horizontal:   6px;
+
+@padding-large-vertical:    10px;
+@padding-large-horizontal:  16px;
+
+@padding-small-vertical:    5px;
+@padding-small-horizontal:  10px;
+
+@padding-xs-vertical:       1px;
+@padding-xs-horizontal:     5px;
+
+@line-height-large:         1.33;
+@line-height-small:         1.5;
+
+@border-radius-base:        4px;
+@border-radius-large:       6px;
+@border-radius-small:       3px;
+
+//** Global color for active items (e.g., navs or dropdowns).
+@component-active-color:    #fff;
+//** Global background color for active items (e.g., navs or dropdowns).
+@component-active-bg:       @brand-primary;
+
+//** Width of the `border` for generating carets that indicator dropdowns.
+@caret-width-base:          4px;
+//** Carets increase slightly in size for larger components.
+@caret-width-large:         5px;
+
+
+//== Tables
+//
+//## Customizes the `.table` component with basic values, each used across all table variations.
+
+//** Padding for ``s and ``s.
+@table-cell-padding:            8px;
+//** Padding for cells in `.table-condensed`.
+@table-condensed-cell-padding:  5px;
+
+//** Default background color used for all tables.
+@table-bg:                      transparent;
+//** Background color used for `.table-striped`.
+@table-bg-accent:               #f9f9f9;
+//** Background color used for `.table-hover`.
+@table-bg-hover:                #f5f5f5;
+@table-bg-active:               @table-bg-hover;
+
+//** Border color for table and cell borders.
+@table-border-color:            #ddd;
+
+
+//== Buttons
+//
+//## For each of Bootstrap's buttons, define text, background and border color.
+
+@btn-font-weight:                normal;
+
+@btn-default-color:              #333;
+@btn-default-bg:                 @gray-lighter;
+@btn-default-border:             @gray-lighter;
+
+@btn-primary-color:              #fff;
+@btn-primary-bg:                 @brand-primary;
+@btn-primary-border:             darken(@btn-primary-bg, 5%);
+
+@btn-success-color:              #fff;
+@btn-success-bg:                 @brand-success;
+@btn-success-border:             darken(@btn-success-bg, 5%);
+
+@btn-info-color:                 #fff;
+@btn-info-bg:                    @brand-info;
+@btn-info-border:                darken(@btn-info-bg, 5%);
+
+@btn-warning-color:              #fff;
+@btn-warning-bg:                 @brand-warning;
+@btn-warning-border:             darken(@btn-warning-bg, 5%);
+
+@btn-danger-color:               #fff;
+@btn-danger-bg:                  @brand-danger;
+@btn-danger-border:              darken(@btn-danger-bg, 5%);
+
+@btn-link-disabled-color:        @gray-light;
+
+
+//== Forms
+//
+//##
+
+//** `` background color
+@input-bg:                       #fff;
+//** `` background color
+@input-bg-disabled:              @gray-lighter;
+
+//** Text color for ``s
+@input-color:                    @gray;
+//** `` border color
+@input-border:                   #b2b2b2;
+
+// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
+//** Default `.form-control` border radius
+@input-border-radius:            @border-radius-base;
+//** Large `.form-control` border radius
+@input-border-radius-large:      @border-radius-large;
+//** Small `.form-control` border radius
+@input-border-radius-small:      @border-radius-small;
+
+//** Border color for inputs on focus
+@input-border-focus:             #d4b33a;
+
+//** Placeholder text color
+@input-color-placeholder:        #999;
+
+//** Default `.form-control` height
+@input-height-base:              (@line-height-computed + (@padding-base-vertical * 2) + 2);
+//** Large `.form-control` height
+@input-height-large:             (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
+//** Small `.form-control` height
+@input-height-small:             (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
+
+@legend-color:                   @gray-dark;
+@legend-border-color:            #e5e5e5;
+
+//** Background color for textual input addons
+@input-group-addon-bg:           @gray-lighter;
+//** Border color for textual input addons
+@input-group-addon-border-color: @input-border;
+
+//** Disabled cursor for form controls and buttons.
+@cursor-disabled:                not-allowed;
+
+
+//== Dropdowns
+//
+//## Dropdown menu container and contents.
+
+//** Background for the dropdown menu.
+@dropdown-bg:                    #fff;
+//** Dropdown menu `border-color`.
+@dropdown-border:                rgba(0,0,0,.15);
+//** Dropdown menu `border-color` **for IE8**.
+@dropdown-fallback-border:       #ccc;
+//** Divider color for between dropdown items.
+@dropdown-divider-bg:            #e5e5e5;
+
+//** Dropdown link text color.
+@dropdown-link-color:            @gray-dark;
+//** Hover color for dropdown links.
+@dropdown-link-hover-color:      darken(@gray-dark, 5%);
+//** Hover background for dropdown links.
+@dropdown-link-hover-bg:         #f5f5f5;
+
+//** Active dropdown menu item text color.
+@dropdown-link-active-color:     @component-active-color;
+//** Active dropdown menu item background color.
+@dropdown-link-active-bg:        @component-active-bg;
+
+//** Disabled dropdown menu item background color.
+@dropdown-link-disabled-color:   @gray-light;
+
+//** Text color for headers within dropdown menus.
+@dropdown-header-color:          @gray-light;
+
+//** Deprecated `@dropdown-caret-color` as of v3.1.0
+@dropdown-caret-color:           #000;
+
+
+//-- Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+//
+// Note: These variables are not generated into the Customizer.
+
+@zindex-navbar:            1000;
+@zindex-dropdown:          1000;
+@zindex-popover:           1060;
+@zindex-tooltip:           1070;
+@zindex-navbar-fixed:      1030;
+@zindex-modal:             1040;
+
+
+//== Media queries breakpoints
+//
+//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
+
+// Extra small screen / phone
+//** Deprecated `@screen-xs` as of v3.0.1
+@screen-xs:                  480px;
+//** Deprecated `@screen-xs-min` as of v3.2.0
+@screen-xs-min:              @screen-xs;
+//** Deprecated `@screen-phone` as of v3.0.1
+@screen-phone:               @screen-xs-min;
+
+// Small screen / tablet
+//** Deprecated `@screen-sm` as of v3.0.1
+@screen-sm:                  768px;
+@screen-sm-min:              @screen-sm;
+//** Deprecated `@screen-tablet` as of v3.0.1
+@screen-tablet:              @screen-sm-min;
+
+// Medium screen / desktop
+//** Deprecated `@screen-md` as of v3.0.1
+@screen-md:                  992px;
+@screen-md-min:              @screen-md;
+//** Deprecated `@screen-desktop` as of v3.0.1
+@screen-desktop:             @screen-md-min;
+
+// Large screen / wide desktop
+//** Deprecated `@screen-lg` as of v3.0.1
+@screen-lg:                  1200px;
+@screen-lg-min:              @screen-lg;
+//** Deprecated `@screen-lg-desktop` as of v3.0.1
+@screen-lg-desktop:          @screen-lg-min;
+
+// So media queries don't overlap when required, provide a maximum
+@screen-xs-max:              (@screen-sm-min - 1);
+@screen-sm-max:              (@screen-md-min - 1);
+@screen-md-max:              (@screen-lg-min - 1);
+
+
+//== Grid system
+//
+//## Define your custom responsive grid.
+
+//** Number of columns in the grid.
+@grid-columns:              12;
+//** Padding between columns. Gets divided in half for the left and right.
+@grid-gutter-width:         30px;
+// Navbar collapse
+//** Point at which the navbar becomes uncollapsed.
+@grid-float-breakpoint:     @screen-sm-min;
+//** Point at which the navbar begins collapsing.
+@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
+
+
+//== Container sizes
+//
+//## Define the maximum width of `.container` for different screen sizes.
+
+// Small screen / tablet
+@container-tablet:             (720px + @grid-gutter-width);
+//** For `@screen-sm-min` and up.
+@container-sm:                 @container-tablet;
+
+// Medium screen / desktop
+@container-desktop:            (940px + @grid-gutter-width);
+//** For `@screen-md-min` and up.
+@container-md:                 @container-desktop;
+
+// Large screen / wide desktop
+@container-large-desktop:      (1140px + @grid-gutter-width);
+//** For `@screen-lg-min` and up.
+@container-lg:                 @container-large-desktop;
+
+
+//== Navbar
+//
+//##
+
+// Basics of a navbar
+@navbar-height:                    100px;
+@navbar-margin-bottom:             0;
+@navbar-border-radius:             @border-radius-base;
+@navbar-padding-horizontal:        0;
+@navbar-padding-vertical:          ((@navbar-height - @line-height-computed) / 2);
+@navbar-collapse-max-height:       340px;
+
+@navbar-default-color:             @gray-base;
+@navbar-default-bg:                #f0db4f;
+@navbar-default-border:            #f0db4f;
+
+// Navbar links
+@navbar-default-link-color:                @navbar-default-color;
+@navbar-default-link-hover-color:          @gray-dark;
+@navbar-default-link-hover-bg:             transparent;
+@navbar-default-link-active-color:         @navbar-default-color;
+@navbar-default-link-active-bg:            transparent;
+@navbar-default-link-disabled-color:       #ccc;
+@navbar-default-link-disabled-bg:          transparent;
+
+// Navbar brand label
+@navbar-default-brand-color:               @navbar-default-link-color;
+@navbar-default-brand-hover-color:         darken(@navbar-default-brand-color, 10%);
+@navbar-default-brand-hover-bg:            transparent;
+
+// Navbar toggle
+@navbar-default-toggle-hover-bg:           #ddd;
+@navbar-default-toggle-icon-bar-bg:        @gray-dark;
+@navbar-default-toggle-border-color:       #ddd;
+
+
+// Inverted navbar
+// Reset inverted navbar basics
+@navbar-inverse-color:                      lighten(@gray-light, 15%);
+@navbar-inverse-bg:                         #222;
+@navbar-inverse-border:                     darken(@navbar-inverse-bg, 10%);
+
+// Inverted navbar links
+@navbar-inverse-link-color:                 lighten(@gray-light, 15%);
+@navbar-inverse-link-hover-color:           #fff;
+@navbar-inverse-link-hover-bg:              transparent;
+@navbar-inverse-link-active-color:          @navbar-inverse-link-hover-color;
+@navbar-inverse-link-active-bg:             darken(@navbar-inverse-bg, 10%);
+@navbar-inverse-link-disabled-color:        #444;
+@navbar-inverse-link-disabled-bg:           transparent;
+
+// Inverted navbar brand label
+@navbar-inverse-brand-color:                @navbar-inverse-link-color;
+@navbar-inverse-brand-hover-color:          #fff;
+@navbar-inverse-brand-hover-bg:             transparent;
+
+// Inverted navbar toggle
+@navbar-inverse-toggle-hover-bg:            #333;
+@navbar-inverse-toggle-icon-bar-bg:         #fff;
+@navbar-inverse-toggle-border-color:        #333;
+
+
+//== Navs
+//
+//##
+
+//=== Shared nav styles
+@nav-link-padding:                          10px 15px;
+@nav-link-hover-bg:                         @gray-lighter;
+
+@nav-disabled-link-color:                   @gray-light;
+@nav-disabled-link-hover-color:             @gray-light;
+
+//== Tabs
+@nav-tabs-border-color:                     #ddd;
+
+@nav-tabs-link-hover-border-color:          @gray-lighter;
+
+@nav-tabs-active-link-hover-bg:             @body-bg;
+@nav-tabs-active-link-hover-color:          @gray;
+@nav-tabs-active-link-hover-border-color:   #ddd;
+
+@nav-tabs-justified-link-border-color:            #ddd;
+@nav-tabs-justified-active-link-border-color:     @body-bg;
+
+//== Pills
+@nav-pills-border-radius:                   @border-radius-base;
+@nav-pills-active-link-hover-bg:            @component-active-bg;
+@nav-pills-active-link-hover-color:         @component-active-color;
+
+
+//== Pagination
+//
+//##
+
+@pagination-color:                     @link-color;
+@pagination-bg:                        #fff;
+@pagination-border:                    #ddd;
+
+@pagination-hover-color:               @link-hover-color;
+@pagination-hover-bg:                  @gray-lighter;
+@pagination-hover-border:              #ddd;
+
+@pagination-active-color:              #fff;
+@pagination-active-bg:                 @brand-primary;
+@pagination-active-border:             @brand-primary;
+
+@pagination-disabled-color:            @gray-light;
+@pagination-disabled-bg:               #fff;
+@pagination-disabled-border:           #ddd;
+
+
+//== Pager
+//
+//##
+
+@pager-bg:                             @pagination-bg;
+@pager-border:                         @pagination-border;
+@pager-border-radius:                  15px;
+
+@pager-hover-bg:                       @pagination-hover-bg;
+
+@pager-active-bg:                      @pagination-active-bg;
+@pager-active-color:                   @pagination-active-color;
+
+@pager-disabled-color:                 @pagination-disabled-color;
+
+
+//== Jumbotron
+//
+//##
+
+@jumbotron-padding:              30px;
+@jumbotron-color:                inherit;
+@jumbotron-bg:                   @gray-lighter;
+@jumbotron-heading-color:        inherit;
+@jumbotron-font-size:            ceil((@font-size-base * 1.5));
+
+
+//== Form states and alerts
+//
+//## Define colors for form feedback states and, by default, alerts.
+
+@state-success-text:             #3c763d;
+@state-success-bg:               #dff0d8;
+@state-success-border:           darken(spin(@state-success-bg, -10), 5%);
+
+@state-info-text:                #31708f;
+@state-info-bg:                  #d9edf7;
+@state-info-border:              darken(spin(@state-info-bg, -10), 7%);
+
+@state-warning-text:             #8a6d3b;
+@state-warning-bg:               #fcf8e3;
+@state-warning-border:           darken(spin(@state-warning-bg, -10), 5%);
+
+@state-danger-text:              #a94442;
+@state-danger-bg:                #f2dede;
+@state-danger-border:            darken(spin(@state-danger-bg, -10), 5%);
+
+
+//== Tooltips
+//
+//##
+
+//** Tooltip max width
+@tooltip-max-width:           200px;
+//** Tooltip text color
+@tooltip-color:               #fff;
+//** Tooltip background color
+@tooltip-bg:                  #000;
+@tooltip-opacity:             .9;
+
+//** Tooltip arrow width
+@tooltip-arrow-width:         5px;
+//** Tooltip arrow color
+@tooltip-arrow-color:         @tooltip-bg;
+
+
+//== Popovers
+//
+//##
+
+//** Popover body background color
+@popover-bg:                          #fff;
+//** Popover maximum width
+@popover-max-width:                   276px;
+//** Popover border color
+@popover-border-color:                rgba(0,0,0,.2);
+//** Popover fallback border color
+@popover-fallback-border-color:       #ccc;
+
+//** Popover title background color
+@popover-title-bg:                    darken(@popover-bg, 3%);
+
+//** Popover arrow width
+@popover-arrow-width:                 10px;
+//** Popover arrow color
+@popover-arrow-color:                 @popover-bg;
+
+//** Popover outer arrow width
+@popover-arrow-outer-width:           (@popover-arrow-width + 1);
+//** Popover outer arrow color
+@popover-arrow-outer-color:           fadein(@popover-border-color, 5%);
+//** Popover outer arrow fallback color
+@popover-arrow-outer-fallback-color:  darken(@popover-fallback-border-color, 20%);
+
+
+//== Labels
+//
+//##
+
+//** Default label background color
+@label-default-bg:            @gray-light;
+//** Primary label background color
+@label-primary-bg:            @brand-primary;
+//** Success label background color
+@label-success-bg:            @brand-success;
+//** Info label background color
+@label-info-bg:               @brand-info;
+//** Warning label background color
+@label-warning-bg:            @brand-warning;
+//** Danger label background color
+@label-danger-bg:             @brand-danger;
+
+//** Default label text color
+@label-color:                 #fff;
+//** Default text color of a linked label
+@label-link-hover-color:      #fff;
+
+
+//== Modals
+//
+//##
+
+//** Padding applied to the modal body
+@modal-inner-padding:         15px;
+
+//** Padding applied to the modal title
+@modal-title-padding:         15px;
+//** Modal title line-height
+@modal-title-line-height:     @line-height-base;
+
+//** Background color of modal content area
+@modal-content-bg:                             #fff;
+//** Modal content border color
+@modal-content-border-color:                   rgba(0,0,0,.2);
+//** Modal content border color **for IE8**
+@modal-content-fallback-border-color:          #999;
+
+//** Modal backdrop background color
+@modal-backdrop-bg:           #000;
+//** Modal backdrop opacity
+@modal-backdrop-opacity:      .5;
+//** Modal header border color
+@modal-header-border-color:   #e5e5e5;
+//** Modal footer border color
+@modal-footer-border-color:   @modal-header-border-color;
+
+@modal-lg:                    900px;
+@modal-md:                    600px;
+@modal-sm:                    300px;
+
+
+//== Alerts
+//
+//## Define alert colors, border radius, and padding.
+
+@alert-padding:               15px;
+@alert-border-radius:         @border-radius-base;
+@alert-link-font-weight:      bold;
+
+@alert-success-bg:            @state-success-bg;
+@alert-success-text:          @state-success-text;
+@alert-success-border:        @state-success-border;
+
+@alert-info-bg:               @state-info-bg;
+@alert-info-text:             @state-info-text;
+@alert-info-border:           @state-info-border;
+
+@alert-warning-bg:            @state-warning-bg;
+@alert-warning-text:          @state-warning-text;
+@alert-warning-border:        @state-warning-border;
+
+@alert-danger-bg:             @state-danger-bg;
+@alert-danger-text:           @state-danger-text;
+@alert-danger-border:         @state-danger-border;
+
+
+//== Progress bars
+//
+//##
+
+//** Background color of the whole progress component
+@progress-bg:                 #f5f5f5;
+//** Progress bar text color
+@progress-bar-color:          #fff;
+//** Variable for setting rounded corners on progress bar.
+@progress-border-radius:      @border-radius-base;
+
+//** Default progress bar color
+@progress-bar-bg:             @brand-primary;
+//** Success progress bar color
+@progress-bar-success-bg:     @brand-success;
+//** Warning progress bar color
+@progress-bar-warning-bg:     @brand-warning;
+//** Danger progress bar color
+@progress-bar-danger-bg:      @brand-danger;
+//** Info progress bar color
+@progress-bar-info-bg:        @brand-info;
+
+
+//== List group
+//
+//##
+
+//** Background color on `.list-group-item`
+@list-group-bg:                 #fff;
+//** `.list-group-item` border color
+@list-group-border:             #ddd;
+//** List group border radius
+@list-group-border-radius:      @border-radius-base;
+
+//** Background color of single list items on hover
+@list-group-hover-bg:           #f5f5f5;
+//** Text color of active list items
+@list-group-active-color:       @component-active-color;
+//** Background color of active list items
+@list-group-active-bg:          @component-active-bg;
+//** Border color of active list elements
+@list-group-active-border:      @list-group-active-bg;
+//** Text color for content within active list items
+@list-group-active-text-color:  lighten(@list-group-active-bg, 40%);
+
+//** Text color of disabled list items
+@list-group-disabled-color:      @gray-light;
+//** Background color of disabled list items
+@list-group-disabled-bg:         @gray-lighter;
+//** Text color for content within disabled list items
+@list-group-disabled-text-color: @list-group-disabled-color;
+
+@list-group-link-color:         #555;
+@list-group-link-hover-color:   @list-group-link-color;
+@list-group-link-heading-color: #333;
+
+
+//== Panels
+//
+//##
+
+@panel-bg:                    #fff;
+@panel-body-padding:          15px;
+@panel-heading-padding:       10px 15px;
+@panel-footer-padding:        @panel-heading-padding;
+@panel-border-radius:         @border-radius-base;
+
+//** Border color for elements within panels
+@panel-inner-border:          #ddd;
+@panel-footer-bg:             #f5f5f5;
+
+@panel-default-text:          @gray-dark;
+@panel-default-border:        #ddd;
+@panel-default-heading-bg:    #f5f5f5;
+
+@panel-primary-text:          #fff;
+@panel-primary-border:        @brand-primary;
+@panel-primary-heading-bg:    @brand-primary;
+
+@panel-success-text:          @state-success-text;
+@panel-success-border:        @state-success-border;
+@panel-success-heading-bg:    @state-success-bg;
+
+@panel-info-text:             @state-info-text;
+@panel-info-border:           @state-info-border;
+@panel-info-heading-bg:       @state-info-bg;
+
+@panel-warning-text:          @state-warning-text;
+@panel-warning-border:        @state-warning-border;
+@panel-warning-heading-bg:    @state-warning-bg;
+
+@panel-danger-text:           @state-danger-text;
+@panel-danger-border:         @state-danger-border;
+@panel-danger-heading-bg:     @state-danger-bg;
+
+
+//== Thumbnails
+//
+//##
+
+//** Padding around the thumbnail image
+@thumbnail-padding:           4px;
+//** Thumbnail background color
+@thumbnail-bg:                @body-bg;
+//** Thumbnail border color
+@thumbnail-border:            #ddd;
+//** Thumbnail border radius
+@thumbnail-border-radius:     @border-radius-base;
+
+//** Custom text color for thumbnail captions
+@thumbnail-caption-color:     @text-color;
+//** Padding around the thumbnail caption
+@thumbnail-caption-padding:   9px;
+
+
+//== Wells
+//
+//##
+
+@well-bg:                     #f5f5f5;
+@well-border:                 darken(@well-bg, 7%);
+
+
+//== Badges
+//
+//##
+
+@badge-color:                 #fff;
+//** Linked badge text color on hover
+@badge-link-hover-color:      #fff;
+@badge-bg:                    @gray-light;
+
+//** Badge text color in active nav link
+@badge-active-color:          @link-color;
+//** Badge background color in active nav link
+@badge-active-bg:             #fff;
+
+@badge-font-weight:           bold;
+@badge-line-height:           1;
+@badge-border-radius:         10px;
+
+
+//== Breadcrumbs
+//
+//##
+
+@breadcrumb-padding-vertical:   8px;
+@breadcrumb-padding-horizontal: 15px;
+//** Breadcrumb background color
+@breadcrumb-bg:                 #f5f5f5;
+//** Breadcrumb text color
+@breadcrumb-color:              #ccc;
+//** Text color of current page in the breadcrumb
+@breadcrumb-active-color:       @gray-light;
+//** Textual separator for between breadcrumb elements
+@breadcrumb-separator:          "/";
+
+
+//== Carousel
+//
+//##
+
+@carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6);
+
+@carousel-control-color:                      #fff;
+@carousel-control-width:                      15%;
+@carousel-control-opacity:                    .5;
+@carousel-control-font-size:                  20px;
+
+@carousel-indicator-active-bg:                #fff;
+@carousel-indicator-border-color:             #fff;
+
+@carousel-caption-color:                      #fff;
+
+
+//== Close
+//
+//##
+
+@close-font-weight:           bold;
+@close-color:                 #000;
+@close-text-shadow:           0 1px 0 #fff;
+
+
+//== Code
+//
+//##
+
+@code-color:                  #c7254e;
+@code-bg:                     #f9f2f4;
+
+@kbd-color:                   #fff;
+@kbd-bg:                      #333;
+
+@pre-bg:                      #f5f5f5;
+@pre-color:                   @gray-dark;
+@pre-border-color:            #ccc;
+@pre-scrollable-max-height:   340px;
+
+
+//== Type
+//
+//##
+
+//** Horizontal offset for forms and lists.
+@component-offset-horizontal: 180px;
+//** Text muted color
+@text-muted:                  @gray-light;
+//** Abbreviations and acronyms border color
+@abbr-border-color:           @gray-light;
+//** Headings small color
+@headings-small-color:        @gray-light;
+//** Blockquote small color
+@blockquote-small-color:      @gray-light;
+//** Blockquote font size
+@blockquote-font-size:        (@font-size-base * 1.25);
+//** Blockquote border color
+@blockquote-border-color:     @gray-lighter;
+//** Page header border color
+@page-header-border-color:    @gray-lighter;
+//** Width of horizontal description list titles
+@dl-horizontal-offset:        @component-offset-horizontal;
+//** Horizontal line color.
+@hr-border:                   #b2b2b2;
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/buttons.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/buttons.less
new file mode 100644
index 00000000..bbb3c620
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/buttons.less
@@ -0,0 +1,61 @@
+//
+// Buttons
+// --------------------------------------------------
+
+
+// Base styles
+// --------------------------------------------------
+
+// Core styles
+.btn {
+  display: inline-block;
+  padding: 5px 6px 4px 8px;
+  margin-top: 0;
+  margin-bottom: 0; // For input.btn
+  font-family: "Open Sans Condensed", sans-serif;
+  font-size: @font-size-base * 1.3;
+  font-weight: 700;
+  text-transform: uppercase;
+  letter-spacing: 1px;
+  line-height: @line-height-computed;
+  text-align: center;
+  vertical-align: middle;
+  touch-action: manipulation;
+  cursor: pointer;
+  background-color: @gray-lighter;
+  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214
+  border: none;
+  border-radius: 3px;
+  .box-shadow(1px 1px 0 rgba(0, 0, 0, 0.3));
+  outline: none !important;
+  white-space: nowrap;
+  .user-select(none);
+
+  &:focus,
+  &.focus {
+    .tab-focus();
+  }
+
+  &:hover,
+  &:focus,
+  &.focus {
+    background-color: lighten(@gray-lighter, 5%);
+    text-decoration: none;
+    .transition(background-color .1s linear);
+  }
+
+  &:active,
+  &.active {
+    outline: 0;
+    padding: 6px 5px 3px 9px;
+  }
+
+  &.disabled,
+  &[disabled],
+  fieldset[disabled] & {
+    cursor: @cursor-disabled;
+    pointer-events: none; // Future-proof disabling of clicks
+    .opacity(65);
+    .box-shadow(1px 1px 0 rgba(0, 0, 0, 0.15));
+  }
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/code.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/code.less
new file mode 100644
index 00000000..33af7555
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/code.less
@@ -0,0 +1,8 @@
+code {
+  margin: 0 2px;
+  padding: 1px 5px;
+  border: 1px solid #E1E1E8;
+  background-color: #F7F7F9;
+  color: @gray-dark;
+  font-size: 85%;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/evaluation-form.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/evaluation-form.less
new file mode 100644
index 00000000..6d505d56
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/evaluation-form.less
@@ -0,0 +1,91 @@
+//
+// Evaluation form
+// --------------------------------------------------
+
+
+.evaluation-form {
+  .clearfix();
+
+  textarea {
+    height: 200px;
+  }
+}
+
+.evaluation-input-output {
+  float: left;
+  width: 65%;
+}
+
+.evaluation-input-clear-button {
+  position: absolute;
+  top: 0;
+  right: 0;
+  width: 15px;
+  height: 15px;
+  margin: 6px;
+  display: inline-block;
+  background: transparent url("../images/clear-text.png") no-repeat scroll 0 0;
+  opacity: 0.75;
+  line-height: 0;
+  vertical-align: bottom;
+  cursor: pointer;
+
+  &:hover {
+   opacity: 1;
+ }
+}
+
+.evaluation-input-clear-button.with-scrollbar {
+  margin-right: 29px;
+}
+
+.evaluation-output {
+  margin-top: 1.5em;
+}
+
+.evaluation-input-field,
+.evaluation-output-field {
+  margin-bottom: 0.5em;
+  .box-sizing(border-box);
+  overflow-x: hidden;
+  line-height: @line-height-computed;
+}
+
+.evaluation-output-field {
+  &[readonly] {
+    cursor: auto;
+    background-color: @input-bg;
+  }
+}
+
+.evaluation-error-header {
+  margin-bottom: @line-height-computed;
+  padding-left: 52px;
+  background: transparent url("../images/icons/48x48/error.png") no-repeat scroll 0 50%;
+  color: @black;
+  font-family: @font-family-sans-serif;
+  font-weight: bold;
+  line-height: 48px;
+  vertical-align: middle;
+}
+
+ul.evaluation-error-list,
+ul.evaluation-warning-list {
+  li {
+    margin-bottom: 1em;
+
+    pre {
+      margin-top: 0.5em;
+      white-space: pre;
+      overflow: auto;
+    }
+  }
+}
+
+ul.evaluation-error-list li pre {
+  background-color: @state-danger-bg;
+}
+
+ul.evaluation-warning-list li pre {
+  background-color: @state-warning-bg;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/forms.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/forms.less
new file mode 100644
index 00000000..2ceb0559
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/forms.less
@@ -0,0 +1,99 @@
+//
+// Forms
+// --------------------------------------------------
+
+// GENERAL STYLES
+// --------------
+
+label {
+  font-weight: normal;
+}
+
+
+// Form controls
+// -------------------------
+
+// Common form controls
+.form-control {
+  line-height: @input-height-base;
+}
+
+.textarea-wrapper {
+  position: relative;
+}
+
+// Form groups
+.form-group {
+  margin-bottom: 0.5em;
+}
+
+// Info and errors
+// --------------------------
+.message-info {
+  border: 1px solid;
+  clear: both;
+  padding: 10px 20px;
+  color: @state-info-text;
+}
+
+.message-error {
+  clear: both;
+  color: @state-danger-text;
+  font-size: 1.1em;
+  font-weight: bold;
+  margin: 20px 0 10px 0;
+}
+
+.message-success {
+  color: @state-success-text;
+  font-size: 1.3em;
+  font-weight: bold;
+  margin: 20px 0 10px 0;
+}
+
+.error {
+  color: @state-danger-text;
+}
+
+// Styles for validation helpers
+// --------------------------
+.validators {
+  font-style: normal;
+  padding-bottom: 0.5em;
+}
+
+.field-validation-error {
+  display: block;
+  padding-bottom: 0.5em;
+  color: @state-danger-text;
+  font-weight: bold;
+}
+
+.field-validation-valid {
+  display: none;
+}
+
+input.input-validation-error,
+textarea.input-validation-error,
+select.input-validation-error
+{
+  border: 1px solid @state-danger-border;
+  background-color: @state-danger-bg;
+  .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
+
+  &:focus {
+    border-color: @state-danger-border;
+    @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@state-danger-text, 20%);
+    .box-shadow(@shadow);
+  }
+}
+
+.validation-summary-errors {
+  color: @state-danger-text;
+  font-weight: bold;
+  font-size: 1.1em;
+}
+
+.validation-summary-valid {
+  display: none;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/icons.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/icons.less
new file mode 100644
index 00000000..4a80f963
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/icons.less
@@ -0,0 +1,36 @@
+//
+// Icons
+// --------------------------------------------------
+
+
+.icon {
+  background: transparent url(../images/icons/32x32/social-media-icons-32.png) no-repeat scroll;
+  display: inline-block;
+  line-height: 0;
+  vertical-align: bottom;
+}
+
+.icon-32 {
+  height: 32px;
+  width: 32px;
+}
+
+.icon-mastodon {
+  background-position: 0 0;
+}
+
+.icon-x {
+  background-position: 0 -32px;
+}
+
+.icon-linkedin {
+  background-position: 0 -64px;
+}
+
+.icon-bluesky {
+  background-position: 0 -96px;
+}
+
+.icon-rss {
+  background-position: 0 -128px;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/layout.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/layout.less
new file mode 100644
index 00000000..35a7f386
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/layout.less
@@ -0,0 +1,128 @@
+html,
+body {
+  margin: 0;
+  height: 100%;
+  /* The html and body elements cannot have any padding or margin */
+}
+
+/* Wrapper for page content to push down footer */
+.l-wrapper {
+  min-height: 100%;
+  height: auto !important;
+  height: 100%;
+  /* Negative indent footer by it's height */
+  margin: 0 auto (-1 * @footer-height);
+}
+
+.l-constrained {
+  margin: 0 auto;
+  padding-left: 20px;
+  padding-right: 20px;
+  max-width: 1200px;
+  min-width: 800px;
+}
+
+.l-header {
+  border-top: solid 10px @black;
+  border-bottom: 1px solid #d4b33a;
+  background-color: #f0db4f;
+
+  .l-constrained {
+    padding-top: 20px;
+    padding-bottom: 20px;
+    .clearfix();
+  }
+}
+
+.l-content {
+  .clearfix();
+}
+
+.l-main-content {
+  float: left;
+  width: 70%;
+}
+
+.l-sidebar {
+  float: right;
+  width: 30%;
+}
+
+/* Set the fixed height of the footer here */
+.l-push,
+.l-footer {
+  height: @footer-height;
+}
+
+.l-footer {
+  width: 100%;
+  background-color: @black;
+  color: @gray-lighter;
+  font-size: @font-size-small;
+  line-height: 100%;
+
+  .l-constrained {
+    padding-top: (@footer-height - 32) / 2;
+    .clearfix();
+  }
+
+  a {
+    color: @gray-lighter;
+    text-decoration: none;
+
+    &:hover,
+    &:active {
+      text-decoration: underline;
+    }
+  }
+}
+
+
+// Header
+// -------------------------
+
+.logo {
+  margin: 0;
+
+  a {
+    display: block;
+    float: left;
+    width: 100px;
+    height: 100px;
+    background: transparent url("../images/jsengineswitcher-logo.png") no-repeat scroll 0 0;
+    text-indent: -9999em;
+  }
+}
+
+.primary-nav {
+  position: relative;
+  left: 40px;
+  float: left;
+}
+
+
+// Footer
+// -------------------------
+
+.l-copyright {
+  float: left;
+    p {
+      margin: 0;
+      padding: 0;
+  }
+}
+
+.l-social {
+  float: right;
+
+  li {
+    display: inline;
+    list-style: none outside none;
+    padding-left: 10px;
+
+    a {
+      text-indent: -9999px;
+      outline: none;
+    }
+  }
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/mixins.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/mixins.less
new file mode 100644
index 00000000..d26eee79
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/mixins.less
@@ -0,0 +1,9 @@
+//
+// Mixins
+// --------------------------------------------------
+
+// NO Drop shadows
+.no-box-shadow {
+  -webkit-box-shadow: none; // iOS <4.3 & Android <4.1
+          box-shadow: none;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/navbar.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/navbar.less
new file mode 100644
index 00000000..95edf3c2
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/navbar.less
@@ -0,0 +1,38 @@
+//
+// Navbars
+// --------------------------------------------------
+
+
+// Wrapper and base class
+//
+// Provide a static navbar from which we expand to create full-width, fixed, and
+// other navbar variations.
+
+.navbar {
+  border: none;
+  border-radius: 0;
+}
+
+// Navbar nav links
+//
+// Builds on top of the `.nav` components with it's own modifier class to make
+// the nav the full height of the horizontal nav (above 768px).
+
+.navbar-nav {
+  float: left;
+  margin: 0 auto;
+
+  > li {
+    > a {
+      float: none;
+      // Vertically center the text given @navbar-height
+      padding: ((@navbar-height - @line-height-computed) / 2) 15px ((@navbar-height - @line-height-computed) / 2);
+      border: 1px solid transparent;
+      font-family: "Open Sans", sans-serif;
+      font-size: 2.0em;
+      text-decoration: none;
+      text-transform: uppercase;
+      outline: none;
+    }
+  }
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/page-socials.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/page-socials.less
new file mode 100644
index 00000000..ea12dc09
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/page-socials.less
@@ -0,0 +1,14 @@
+.page-socials {
+  min-height: 60px;
+  margin-top: 20px;
+  padding-top: 15px;
+  border-top: 1px solid #b2b2b2;
+}
+
+.share-buttons {
+  li {
+    margin-right: 15px;
+    vertical-align: top;
+    line-height: normal;
+  }
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/type.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/type.less
new file mode 100644
index 00000000..02d69502
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/type.less
@@ -0,0 +1,24 @@
+//
+// Typography
+// --------------------------------------------------
+
+// Headings
+// -------------------------
+
+h1, h2, h3 {
+  margin-top: 10px;
+}
+
+h1, h2 {
+  line-height: 40px;
+}
+
+h2 {
+  color: @black;
+  font-weight: bold;
+}
+
+h3 {
+  color: @gray-dark;
+  line-height: @line-height-computed * 1.25;
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/variables.less b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/variables.less
new file mode 100644
index 00000000..498c9796
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot/styles/variables.less
@@ -0,0 +1,7 @@
+// Colors
+// -------------------------
+@black:                 #1d0d0d;
+
+// Footer
+// -------------------------
+@footer-height:                    55px;
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/Helpers/CommonExtensions.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/Helpers/CommonExtensions.cs
new file mode 100644
index 00000000..d58feadb
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/Helpers/CommonExtensions.cs
@@ -0,0 +1,16 @@
+using System.Text.RegularExpressions;
+
+using Microsoft.AspNetCore.Html;
+using Microsoft.AspNetCore.Mvc.Rendering;
+
+namespace JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.Helpers
+{
+	public static class CommonExtensions
+	{
+		public static HtmlString EncodedReplace(this IHtmlHelper htmlHelper, string input,
+			string pattern, string replacement)
+		{
+			return new HtmlString(Regex.Replace(htmlHelper.Encode(input), pattern, replacement));
+		}
+	}
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.csproj
new file mode 100644
index 00000000..8ad369ff
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.csproj
@@ -0,0 +1,56 @@
+
+
+  
+    JS Engine Switcher: Infrastructure for ASP.NET Core Samples
+    3.30.2
+    net451;netstandard1.6;netstandard2.0;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0
+    1.6.0
+    Library
+    true
+    $(NoWarn);NETSDK1215;NU1902;NU1903;NU1904
+    false
+    false
+    false
+  
+
+  
+
+  
+    
+    
+  
+
+  
+    
+    
+  
+
+  
+    
+  
+
+  
+    
+  
+
+  
+    
+  
+
+  
+    
+  
+
+  
+    
+  
+
+  
+    
+  
+
+  
+    
+  
+
+
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/TagHelpers/ConditionalCommentTagHelper.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/TagHelpers/ConditionalCommentTagHelper.cs
new file mode 100644
index 00000000..22e7e9bb
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/TagHelpers/ConditionalCommentTagHelper.cs
@@ -0,0 +1,78 @@
+using System;
+
+using Microsoft.AspNetCore.Razor.TagHelpers;
+
+namespace JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers
+{
+	[HtmlTargetElement("conditional-comment")]
+	public class ConditionalCommentTagHelper : TagHelper
+	{
+		[HtmlAttributeName("type")]
+		public ConditionalCommentType CommentType { get; set; }
+
+		[HtmlAttributeName("expression")]
+		public string Expression { get; set; }
+
+
+		public override void Process(TagHelperContext context, TagHelperOutput output)
+		{
+			output.TagName = null;
+
+			ConditionalCommentType type = CommentType;
+
+			string ifCommentStartPart;
+			string ifCommentEndPart;
+
+			switch (type)
+			{
+				case ConditionalCommentType.Hidden:
+					ifCommentStartPart = "";
+
+					break;
+				case ConditionalCommentType.RevealedValidatingSimplified:
+					ifCommentStartPart = "";
+
+					break;
+				case ConditionalCommentType.Revealed:
+					ifCommentStartPart = "";
+
+					break;
+				default:
+					throw new NotSupportedException();
+			}
+
+			TagHelperContent preContent = output.PreContent;
+			preContent.AppendHtml(ifCommentStartPart);
+			preContent.AppendHtml(Expression);
+			preContent.AppendHtml(ifCommentEndPart);
+
+			string endIfComment;
+
+			switch (type)
+			{
+				case ConditionalCommentType.Hidden:
+					endIfComment = "";
+					break;
+				case ConditionalCommentType.RevealedValidating:
+				case ConditionalCommentType.RevealedValidatingSimplified:
+					endIfComment = "";
+					break;
+				case ConditionalCommentType.Revealed:
+					endIfComment = "";
+					break;
+				default:
+					throw new NotSupportedException();
+			}
+
+			output.PostContent.AppendHtml(endIfComment);
+		}
+	}
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/TagHelpers/ConditionalCommentType.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/TagHelpers/ConditionalCommentType.cs
new file mode 100644
index 00000000..485194ba
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure/TagHelpers/ConditionalCommentType.cs
@@ -0,0 +1,10 @@
+namespace JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers
+{
+	public enum ConditionalCommentType
+	{
+		Hidden,
+		Revealed,
+		RevealedValidating,
+		RevealedValidatingSimplified
+	}
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Controllers/HomeController.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Controllers/HomeController.cs
new file mode 100644
index 00000000..ce25e2d1
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Controllers/HomeController.cs
@@ -0,0 +1,80 @@
+using System.Threading.Tasks;
+
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Html;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+
+using JavaScriptEngineSwitcher.Sample.Logic.Models;
+using JavaScriptEngineSwitcher.Sample.Logic.Services;
+
+namespace JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1.Controllers
+{
+	public class HomeController : Controller
+	{
+		private readonly FileContentService _fileContentService;
+		private readonly JsEvaluationService _jsEvaluationService;
+
+
+		public HomeController(
+			IConfigurationRoot configuration,
+			IHostingEnvironment hostingEnvironment,
+			JsEvaluationService jsEvaluationService)
+		{
+			string textContentDirectoryPath = configuration
+				.GetSection("jsengineswitcher")
+				.GetSection("Samples")["TextContentDirectoryPath"]
+				;
+
+			_fileContentService = new FileContentService(textContentDirectoryPath, hostingEnvironment);
+			_jsEvaluationService = jsEvaluationService;
+		}
+
+
+		[ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")]
+		public IActionResult Index()
+		{
+			ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("index.html"));
+
+			return View();
+		}
+
+		[HttpGet]
+		[ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")]
+		public IActionResult Demo()
+		{
+			var model = _jsEvaluationService.GetInitializationData();
+
+			return View(model);
+		}
+
+		[HttpPost]
+		public async Task Demo(JsEvaluationViewModel editedModel)
+		{
+			var model = _jsEvaluationService.GetInitializationData();
+			await TryUpdateModelAsync(model, string.Empty, m => m.EngineName, m=> m.Expression);
+
+			if (ModelState.IsValid)
+			{
+				model = _jsEvaluationService.Evaluate(model);
+
+				ModelState.Clear();
+			}
+
+			return View(model);
+		}
+
+		[ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")]
+		public IActionResult Contact()
+		{
+			ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("contact.html"));
+
+			return View();
+		}
+
+		public IActionResult Error()
+		{
+			return View();
+		}
+	}
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1.csproj
new file mode 100644
index 00000000..08c70ce0
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1.csproj
@@ -0,0 +1,55 @@
+
+
+  
+    JS Engine Switcher: Sample ASP.NET Core 1.0 MVC 1 Site
+    3.30.2
+    netcoreapp1.0
+    1.0.16
+    Exe
+    false
+    $(NoWarn);NU1903;NU1904
+    true
+    true
+    false
+    false
+  
+
+  
+
+  
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+
+    
+    
+    
+    
+    
+    
+  
+
+  
+    
+      PreserveNewest
+    
+  
+
+  
+
+
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Program.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Program.cs
new file mode 100644
index 00000000..f2bd4508
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Program.cs
@@ -0,0 +1,27 @@
+using System.IO;
+
+using Microsoft.AspNetCore.Hosting;
+
+namespace JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1
+{
+	public class Program
+	{
+		public static void Main(string[] args)
+		{
+			string currentDirectory = Directory.GetCurrentDirectory();
+			var host = new WebHostBuilder()
+				.UseKestrel()
+				.UseContentRoot(currentDirectory)
+				.UseWebRoot(Path.Combine(
+					currentDirectory,
+					"../JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot"
+				))
+				.UseIISIntegration()
+				.UseStartup()
+				.Build()
+				;
+
+			host.Run();
+		}
+	}
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Properties/launchSettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Properties/launchSettings.json
new file mode 100644
index 00000000..532b02b6
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:15463/",
+      "sslPort": 0
+    }
+  },
+  "profiles": {
+    "JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      },
+      "applicationUrl": "http://localhost:15465/"
+    },
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    }
+  }
+}
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Startup.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Startup.cs
new file mode 100644
index 00000000..4d8735af
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Startup.cs
@@ -0,0 +1,108 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+using JavaScriptEngineSwitcher.ChakraCore;
+using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection;
+using JavaScriptEngineSwitcher.Msie;
+using JavaScriptEngineSwitcher.Sample.Logic.Services;
+using JavaScriptEngineSwitcher.Vroom;
+
+namespace JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1
+{
+	public class Startup
+	{
+		/// 
+		/// Gets or sets a instance of hosting environment
+		/// 
+		public IHostingEnvironment HostingEnvironment
+		{
+			get;
+			set;
+		}
+
+		public IConfigurationRoot Configuration
+		{
+			get;
+			set;
+		}
+
+
+		public Startup(IHostingEnvironment env)
+		{
+			HostingEnvironment = env;
+
+			var builder = new ConfigurationBuilder()
+				.SetBasePath(env.ContentRootPath)
+				.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
+				.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
+				.AddEnvironmentVariables();
+			Configuration = builder.Build();
+		}
+
+		// This method gets called by the runtime. Use this method to add services to the container.
+		public void ConfigureServices(IServiceCollection services)
+		{
+			services.AddSingleton(Configuration);
+
+			// Add JavaScriptEngineSwitcher services to the services container.
+			services.AddJsEngineSwitcher(options =>
+			{
+				options.AllowCurrentProperty = false;
+				options.DefaultEngineName = ChakraCoreJsEngine.EngineName;
+			})
+				.AddChakraCore()
+				.AddMsie(options =>
+				{
+					options.EngineMode = JsEngineMode.ChakraIeJsRt;
+				})
+				.AddVroom()
+				;
+
+			// Add framework services.
+			services.AddMvc(options =>
+			{
+				options.CacheProfiles.Add("CacheCompressedContent5Minutes",
+					new CacheProfile
+					{
+						NoStore = HostingEnvironment.IsDevelopment(),
+						Duration = 300,
+						Location = ResponseCacheLocation.Client,
+						VaryByHeader = "Accept-Encoding"
+					}
+				);
+			});
+
+			// Add JavaScriptEngineSwitcher sample services to the services container.
+			services.AddSingleton();
+		}
+
+		// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+		public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
+		{
+			loggerFactory.AddConsole(Configuration.GetSection("Logging"));
+			loggerFactory.AddDebug();
+
+			if (env.IsDevelopment())
+			{
+				app.UseDeveloperExceptionPage();
+			}
+			else
+			{
+				app.UseExceptionHandler("/Home/Error");
+			}
+
+			app.UseStaticFiles();
+
+			app.UseMvc(routes =>
+			{
+				routes.MapRoute(
+					name: "default",
+					template: "{controller=Home}/{action=Index}/{id?}");
+			});
+		}
+	}
+}
\ No newline at end of file
diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Home/Contact.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Home/Contact.cshtml
new file mode 100644
index 00000000..58785ebe
--- /dev/null
+++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Home/Contact.cshtml
@@ -0,0 +1,8 @@
+@{
+	ViewBag.Title = "Contact";
+}
+
+
+

@ViewBag.Title

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Home/Demo.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Home/Demo.cshtml new file mode 100644 index 00000000..befb8f43 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Home/Demo.cshtml @@ -0,0 +1,67 @@ +@using JavaScriptEngineSwitcher.Sample.Logic.Models +@using JavaScriptEngineSwitcher.Sample.Resources + +@model JsEvaluationViewModel + +@{ + ViewBag.Title = "Demo"; +} + +

@ViewBag.Title

+ +
+
+
+
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ + @if (Model.Result != null) + { +
+ @if (Model.Result.Errors.Count == 0) + { +
+ + +
+ } + else + { + await Html.RenderPartialAsync("_JsEvaluationErrorList", Model.Result.Errors); + } +
+ } +
+
+
+ +@section Scripts { + + + + + + + + + +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Home/Index.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Home/Index.cshtml new file mode 100644 index 00000000..a9c2cfb1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = string.Empty; +} + +
+

Project Description

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Shared/Error.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Shared/Error.cshtml new file mode 100644 index 00000000..d7f29efe --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Shared/Error.cshtml @@ -0,0 +1,14 @@ +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. +

\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Shared/_JsEvaluationErrorList.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Shared/_JsEvaluationErrorList.cshtml new file mode 100644 index 00000000..9646764d --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Shared/_JsEvaluationErrorList.cshtml @@ -0,0 +1,23 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models + +@model IList + +

Found @Model.Count error(s):

+
    + @foreach (var error in Model) + { +
  • + @error.EngineFullName
    + @if (error.LineNumber > 0) + { + Line @error.LineNumber, Column @error.ColumnNumber
    + } + @Html.EncodedReplace(@error.Message, "\n\r?", "
    ") + @if (!string.IsNullOrWhiteSpace(error.SourceFragment)) + { +
    @error.SourceFragment
    + } +
  • + } +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Shared/_Layout.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..b60019c1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/Shared/_Layout.cshtml @@ -0,0 +1,107 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers + + + + + + + + + @if (!string.IsNullOrEmpty(ViewBag.Title)) {<text>@ViewBag.Title | </text>}JavaScriptEngineSwitcher Sample ASP.NET Core 1.0 MVC 1 Site + + + + + + + + + + + + +
+ + + + + +
+
+ @RenderBody() +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @RenderSection("scripts", required: false) + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/_ViewImports.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/_ViewImports.cshtml new file mode 100644 index 00000000..5364be02 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" +@addTagHelper "*, JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure" \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/_ViewStart.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/_ViewStart.cshtml new file mode 100644 index 00000000..817a9134 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/appsettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/appsettings.json new file mode 100644 index 00000000..7124dec4 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/appsettings.json @@ -0,0 +1,16 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + + "JsEngineSwitcher": { + "Samples": { + "TextContentDirectoryPath": "../SharedData/text-content" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/web.config b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/web.config new file mode 100644 index 00000000..76e68eea --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1.Mvc1/web.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Controllers/HomeController.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Controllers/HomeController.cs new file mode 100644 index 00000000..57f376ff --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Controllers/HomeController.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; +using System.Threading.Tasks; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +using JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Services; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9.Controllers +{ + public class HomeController : Controller + { + private readonly FileContentService _fileContentService; + private readonly JsEvaluationService _jsEvaluationService; + + + public HomeController( + IConfigurationRoot configuration, + IWebHostEnvironment hostingEnvironment, + JsEvaluationService jsEvaluationService) + { + string textContentDirectoryPath = configuration + .GetSection("jsengineswitcher") + .GetSection("Samples")["TextContentDirectoryPath"] + ; + + _fileContentService = new FileContentService(textContentDirectoryPath, hostingEnvironment); + _jsEvaluationService = jsEvaluationService; + } + + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Index() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("index.html")); + + return View(); + } + + [HttpGet] + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Demo() + { + var model = _jsEvaluationService.GetInitializationData(); + + return View(model); + } + + [HttpPost] + public async Task Demo(JsEvaluationViewModel editedModel) + { + var model = _jsEvaluationService.GetInitializationData(); + await TryUpdateModelAsync(model, string.Empty, m => m.EngineName, m=> m.Expression); + + if (ModelState.IsValid) + { + model = _jsEvaluationService.Evaluate(model); + + ModelState.Clear(); + } + + return View(model); + } + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Contact() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("contact.html")); + + return View(); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10.csproj new file mode 100644 index 00000000..3d8c1ebc --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10.csproj @@ -0,0 +1,49 @@ + + + + JS Engine Switcher: Sample ASP.NET Core 10.0 MVC 10 Site + 3.30.2 + net10.0 + enable + Exe + true + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Models/ErrorViewModel.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Models/ErrorViewModel.cs new file mode 100644 index 00000000..20cc9827 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Models/ErrorViewModel.cs @@ -0,0 +1,9 @@ +namespace JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9.Models +{ + public class ErrorViewModel + { + public string RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Program.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Program.cs new file mode 100644 index 00000000..457ccf63 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Program.cs @@ -0,0 +1,105 @@ +using Jering.Javascript.NodeJS; +using Microsoft.AspNetCore.Mvc; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.Node; +using JavaScriptEngineSwitcher.Sample.Logic.Services; +using JavaScriptEngineSwitcher.V8; +using JavaScriptEngineSwitcher.Vroom; +using JavaScriptEngineSwitcher.Yantra; + +var builder = WebApplication.CreateBuilder(new WebApplicationOptions() { + WebRootPath = Path.Combine( + Directory.GetCurrentDirectory(), + "../JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot" + ) +}); +var env = builder.Environment; +var configuration = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables() + .Build() + ; + +#region Configure services + +IServiceCollection services = builder.Services; + +services.AddSingleton(configuration); + +// Add Jering Node.js service to the services container. +services.AddNodeJS(); + +// Add JavaScriptEngineSwitcher services to the services container. +services.AddJsEngineSwitcher(options => +{ + options.AllowCurrentProperty = false; + options.DefaultEngineName = ChakraCoreJsEngine.EngineName; +}) + .AddChakraCore() + .AddJint() + .AddJurassic() + .AddMsie(options => + { + options.EngineMode = JsEngineMode.ChakraIeJsRt; + }) + .AddNiL() + .AddNode(services) + .AddV8() + .AddVroom() + .AddYantra() + ; + +services.Configure(options => +{ + options.CacheProfiles.Add("CacheCompressedContent5Minutes", + new CacheProfile + { + NoStore = builder.Environment.IsDevelopment(), + Duration = 300, + Location = ResponseCacheLocation.Client, + VaryByHeader = "Accept-Encoding" + } + ); +}); + +// Add framework services. +services.AddControllersWithViews(); + +// Add JavaScriptEngineSwitcher sample services to the services container. +services.AddSingleton(); + +#endregion + +#region Configure the HTTP request pipeline + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} +else +{ + app.UseExceptionHandler("/Home/Error"); +} + +app.UseRouting(); + +app.MapStaticAssets(); + +app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}") + .WithStaticAssets(); + +#endregion + +app.Run(); \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Properties/launchSettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Properties/launchSettings.json new file mode 100644 index 00000000..2c93180d --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Properties/launchSettings.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:61316/", + "sslPort": 44396 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5169" + }, + "https": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7103;http://localhost:5169" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Home/Contact.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Home/Contact.cshtml new file mode 100644 index 00000000..58785ebe --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Home/Contact.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Contact"; +} + +
+

@ViewBag.Title

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Home/Demo.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Home/Demo.cshtml new file mode 100644 index 00000000..befb8f43 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Home/Demo.cshtml @@ -0,0 +1,67 @@ +@using JavaScriptEngineSwitcher.Sample.Logic.Models +@using JavaScriptEngineSwitcher.Sample.Resources + +@model JsEvaluationViewModel + +@{ + ViewBag.Title = "Demo"; +} + +

@ViewBag.Title

+ +
+
+
+
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ + @if (Model.Result != null) + { +
+ @if (Model.Result.Errors.Count == 0) + { +
+ + +
+ } + else + { + await Html.RenderPartialAsync("_JsEvaluationErrorList", Model.Result.Errors); + } +
+ } +
+
+
+ +@section Scripts { + + + + + + + + + +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Home/Index.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Home/Index.cshtml new file mode 100644 index 00000000..a9c2cfb1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = string.Empty; +} + +
+

Project Description

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Shared/Error.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Shared/Error.cshtml new file mode 100644 index 00000000..b568b9f2 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Shared/Error.cshtml @@ -0,0 +1,28 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9.Models + +@model ErrorViewModel + +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +@if (Model.ShowRequestId) +{ +

+ Request ID: @Model.RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Shared/_JsEvaluationErrorList.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Shared/_JsEvaluationErrorList.cshtml new file mode 100644 index 00000000..9e0601d6 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Shared/_JsEvaluationErrorList.cshtml @@ -0,0 +1,23 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models + +@model IList + +

Found @Model.Count error(s):

+
    + @foreach (var error in Model) + { +
  • + @error.EngineFullName
    + @if (error.LineNumber > 0) + { + Line @error.LineNumber, Column @error.ColumnNumber
    + } + @Html.EncodedReplace(@error.Message, "\n\r?", "
    ") + @if (!string.IsNullOrWhiteSpace(error.SourceFragment)) + { +
    @error.SourceFragment
    + } +
  • + } +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Shared/_Layout.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..62e3d72c --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/Shared/_Layout.cshtml @@ -0,0 +1,107 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers + + + + + + + + + @if (!string.IsNullOrEmpty(ViewBag.Title)) {<text>@ViewBag.Title | </text>}JavaScriptEngineSwitcher Sample ASP.NET Core 10.0 MVC 10 Site + + + + + + + + + + + + +
+ + + + + +
+
+ @RenderBody() +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @await RenderSectionAsync("scripts", required: false) + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/_ViewImports.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/_ViewImports.cshtml new file mode 100644 index 00000000..dcad619c --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/_ViewStart.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/_ViewStart.cshtml new file mode 100644 index 00000000..817a9134 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/appsettings.Development.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/appsettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/appsettings.json new file mode 100644 index 00000000..ed44b7ae --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore10.Mvc10/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + + "JsEngineSwitcher": { + "Samples": { + "TextContentDirectoryPath": "../SharedData/text-content" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Controllers/HomeController.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Controllers/HomeController.cs new file mode 100644 index 00000000..dfd818ad --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Controllers/HomeController.cs @@ -0,0 +1,80 @@ +using System.Threading.Tasks; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +using JavaScriptEngineSwitcher.Sample.Logic.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Services; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1.Controllers +{ + public class HomeController : Controller + { + private readonly FileContentService _fileContentService; + private readonly JsEvaluationService _jsEvaluationService; + + + public HomeController( + IConfigurationRoot configuration, + IHostingEnvironment hostingEnvironment, + JsEvaluationService jsEvaluationService) + { + string textContentDirectoryPath = configuration + .GetSection("jsengineswitcher") + .GetSection("Samples")["TextContentDirectoryPath"] + ; + + _fileContentService = new FileContentService(textContentDirectoryPath, hostingEnvironment); + _jsEvaluationService = jsEvaluationService; + } + + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Index() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("index.html")); + + return View(); + } + + [HttpGet] + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Demo() + { + var model = _jsEvaluationService.GetInitializationData(); + + return View(model); + } + + [HttpPost] + public async Task Demo(JsEvaluationViewModel editedModel) + { + var model = _jsEvaluationService.GetInitializationData(); + await TryUpdateModelAsync(model, string.Empty, m => m.EngineName, m=> m.Expression); + + if (ModelState.IsValid) + { + model = _jsEvaluationService.Evaluate(model); + + ModelState.Clear(); + } + + return View(model); + } + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Contact() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("contact.html")); + + return View(); + } + + public IActionResult Error() + { + return View(); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1.csproj new file mode 100644 index 00000000..8d4e9283 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1.csproj @@ -0,0 +1,54 @@ + + + + JS Engine Switcher: Sample ASP.NET Core 1.0 Full MVC 1 Site + 3.30.2 + net451 + Exe + true + $(NoWarn);NU1903;NU1904 + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Program.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Program.cs new file mode 100644 index 00000000..56e599bf --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Program.cs @@ -0,0 +1,27 @@ +using System.IO; + +using Microsoft.AspNetCore.Hosting; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1 +{ + public class Program + { + public static void Main(string[] args) + { + string currentDirectory = Directory.GetCurrentDirectory(); + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(currentDirectory) + .UseWebRoot(Path.Combine( + currentDirectory, + "../JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot" + )) + .UseIISIntegration() + .UseStartup() + .Build() + ; + + host.Run(); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Properties/launchSettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Properties/launchSettings.json new file mode 100644 index 00000000..e823558d --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:11752/", + "sslPort": 0 + } + }, + "profiles": { + "JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Startup.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Startup.cs new file mode 100644 index 00000000..970f4b07 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Startup.cs @@ -0,0 +1,116 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.Sample.Logic.Services; +using JavaScriptEngineSwitcher.Vroom; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1 +{ + public class Startup + { + /// + /// Gets or sets a instance of hosting environment + /// + public IHostingEnvironment HostingEnvironment + { + get; + set; + } + + public IConfigurationRoot Configuration + { + get; + set; + } + + + public Startup(IHostingEnvironment env) + { + HostingEnvironment = env; + + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables(); + Configuration = builder.Build(); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(Configuration); + + // Add JavaScriptEngineSwitcher services to the services container. + services.AddJsEngineSwitcher(options => + { + options.AllowCurrentProperty = false; + options.DefaultEngineName = ChakraCoreJsEngine.EngineName; + }) + .AddChakraCore() + .AddJurassic() + .AddMsie(options => + { + options.EngineMode = JsEngineMode.ChakraIeJsRt; + }) + .AddVroom() + ; + + // Add framework services. + var manager = new ApplicationPartManager(); + manager.ApplicationParts.Add(new AssemblyPart(typeof(Startup).Assembly)); + + services.AddSingleton(manager); + + services.AddMvc(options => + { + options.CacheProfiles.Add("CacheCompressedContent5Minutes", + new CacheProfile + { + NoStore = HostingEnvironment.IsDevelopment(), + Duration = 300, + Location = ResponseCacheLocation.Client, + VaryByHeader = "Accept-Encoding" + } + ); + }); + + // Add JavaScriptEngineSwitcher sample services to the services container. + services.AddSingleton(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(Configuration.GetSection("Logging")); + loggerFactory.AddDebug(); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Home/Error"); + } + + app.UseStaticFiles(); + + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Home/Contact.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Home/Contact.cshtml new file mode 100644 index 00000000..58785ebe --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Home/Contact.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Contact"; +} + +
+

@ViewBag.Title

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Home/Demo.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Home/Demo.cshtml new file mode 100644 index 00000000..befb8f43 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Home/Demo.cshtml @@ -0,0 +1,67 @@ +@using JavaScriptEngineSwitcher.Sample.Logic.Models +@using JavaScriptEngineSwitcher.Sample.Resources + +@model JsEvaluationViewModel + +@{ + ViewBag.Title = "Demo"; +} + +

@ViewBag.Title

+ +
+
+
+
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ + @if (Model.Result != null) + { +
+ @if (Model.Result.Errors.Count == 0) + { +
+ + +
+ } + else + { + await Html.RenderPartialAsync("_JsEvaluationErrorList", Model.Result.Errors); + } +
+ } +
+
+
+ +@section Scripts { + + + + + + + + + +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Home/Index.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Home/Index.cshtml new file mode 100644 index 00000000..a9c2cfb1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = string.Empty; +} + +
+

Project Description

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Shared/Error.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Shared/Error.cshtml new file mode 100644 index 00000000..d7f29efe --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Shared/Error.cshtml @@ -0,0 +1,14 @@ +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. +

\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Shared/_JsEvaluationErrorList.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Shared/_JsEvaluationErrorList.cshtml new file mode 100644 index 00000000..9646764d --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Shared/_JsEvaluationErrorList.cshtml @@ -0,0 +1,23 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models + +@model IList + +

Found @Model.Count error(s):

+
    + @foreach (var error in Model) + { +
  • + @error.EngineFullName
    + @if (error.LineNumber > 0) + { + Line @error.LineNumber, Column @error.ColumnNumber
    + } + @Html.EncodedReplace(@error.Message, "\n\r?", "
    ") + @if (!string.IsNullOrWhiteSpace(error.SourceFragment)) + { +
    @error.SourceFragment
    + } +
  • + } +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Shared/_Layout.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..34baa99f --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/Shared/_Layout.cshtml @@ -0,0 +1,107 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers + + + + + + + + + @if (!string.IsNullOrEmpty(ViewBag.Title)) {<text>@ViewBag.Title | </text>}JavaScriptEngineSwitcher Sample ASP.NET Core 1.0 Full MVC 1 Site + + + + + + + + + + + + +
+ + + + + +
+
+ @RenderBody() +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @RenderSection("scripts", required: false) + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/_ViewImports.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/_ViewImports.cshtml new file mode 100644 index 00000000..5364be02 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" +@addTagHelper "*, JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure" \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/_ViewStart.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/_ViewStart.cshtml new file mode 100644 index 00000000..817a9134 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/app.config b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/app.config new file mode 100644 index 00000000..8e69f952 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/app.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/appsettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/appsettings.json new file mode 100644 index 00000000..7124dec4 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/appsettings.json @@ -0,0 +1,16 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + + "JsEngineSwitcher": { + "Samples": { + "TextContentDirectoryPath": "../SharedData/text-content" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/web.config b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/web.config new file mode 100644 index 00000000..76e68eea --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore1Full.Mvc1/web.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Controllers/HomeController.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Controllers/HomeController.cs new file mode 100644 index 00000000..6ed67b43 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Controllers/HomeController.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; +using System.Threading.Tasks; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +using JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Services; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21.Controllers +{ + public class HomeController : Controller + { + private readonly FileContentService _fileContentService; + private readonly JsEvaluationService _jsEvaluationService; + + + public HomeController( + IConfigurationRoot configuration, + IHostingEnvironment hostingEnvironment, + JsEvaluationService jsEvaluationService) + { + string textContentDirectoryPath = configuration + .GetSection("jsengineswitcher") + .GetSection("Samples")["TextContentDirectoryPath"] + ; + + _fileContentService = new FileContentService(textContentDirectoryPath, hostingEnvironment); + _jsEvaluationService = jsEvaluationService; + } + + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Index() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("index.html")); + + return View(); + } + + [HttpGet] + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Demo() + { + var model = _jsEvaluationService.GetInitializationData(); + + return View(model); + } + + [HttpPost] + public async Task Demo(JsEvaluationViewModel editedModel) + { + var model = _jsEvaluationService.GetInitializationData(); + await TryUpdateModelAsync(model, string.Empty, m => m.EngineName, m=> m.Expression); + + if (ModelState.IsValid) + { + model = _jsEvaluationService.Evaluate(model); + + ModelState.Clear(); + } + + return View(model); + } + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Contact() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("contact.html")); + + return View(); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21.csproj new file mode 100644 index 00000000..620e4ee2 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21.csproj @@ -0,0 +1,40 @@ + + + + JS Engine Switcher: Sample ASP.NET Core 2.1 MVC 2.1 Site + 3.30.2 + netcoreapp2.1 + Exe + true + $(NoWarn);NU1902;NU1903;NU1904 + true + true + true + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Models/ErrorViewModel.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Models/ErrorViewModel.cs new file mode 100644 index 00000000..9149c488 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Models/ErrorViewModel.cs @@ -0,0 +1,11 @@ +using System; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21.Models +{ + public class ErrorViewModel + { + public string RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Program.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Program.cs new file mode 100644 index 00000000..3dcfa5fc --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Program.cs @@ -0,0 +1,24 @@ +using System.IO; + +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21 +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseWebRoot(Path.Combine( + Directory.GetCurrentDirectory(), + "../JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot" + )) + .UseStartup() + ; + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Properties/launchSettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Properties/launchSettings.json new file mode 100644 index 00000000..def3944c --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:1972/", + "sslPort": 0 + } + }, + "profiles": { + "JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:1973/" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Startup.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Startup.cs new file mode 100644 index 00000000..5a5b6be7 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Startup.cs @@ -0,0 +1,121 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.Sample.Logic.Services; +using JavaScriptEngineSwitcher.Vroom; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21 +{ + public class Startup + { + /// + /// Gets or sets a instance of hosting environment + /// + public IHostingEnvironment HostingEnvironment + { + get; + set; + } + + public IConfigurationRoot Configuration + { + get; + set; + } + + + public Startup(IHostingEnvironment env) + { + HostingEnvironment = env; + + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables(); + Configuration = builder.Build(); + } + + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(Configuration); + + // Add JavaScriptEngineSwitcher services to the services container. + services.AddJsEngineSwitcher(options => + { + options.AllowCurrentProperty = false; + options.DefaultEngineName = ChakraCoreJsEngine.EngineName; + }) + .AddChakraCore() + .AddJint() + .AddJurassic() + .AddMsie(options => + { + options.EngineMode = JsEngineMode.ChakraIeJsRt; + }) + .AddVroom() + ; + + services.Configure(options => + { + // This lambda determines whether user consent for non-essential cookies is needed for a given request. + options.CheckConsentNeeded = context => true; + options.MinimumSameSitePolicy = SameSiteMode.None; + }); + + // Add framework services. + services.AddMvc(options => + { + options.CacheProfiles.Add("CacheCompressedContent5Minutes", + new CacheProfile + { + NoStore = HostingEnvironment.IsDevelopment(), + Duration = 300, + Location = ResponseCacheLocation.Client, + VaryByHeader = "Accept-Encoding" + } + ); + }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1); + + // Add JavaScriptEngineSwitcher sample services to the services container. + services.AddSingleton(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(Configuration.GetSection("Logging")); + loggerFactory.AddDebug(); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Home/Error"); + } + + app.UseStaticFiles(); + + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Home/Contact.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Home/Contact.cshtml new file mode 100644 index 00000000..58785ebe --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Home/Contact.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Contact"; +} + +
+

@ViewBag.Title

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Home/Demo.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Home/Demo.cshtml new file mode 100644 index 00000000..befb8f43 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Home/Demo.cshtml @@ -0,0 +1,67 @@ +@using JavaScriptEngineSwitcher.Sample.Logic.Models +@using JavaScriptEngineSwitcher.Sample.Resources + +@model JsEvaluationViewModel + +@{ + ViewBag.Title = "Demo"; +} + +

@ViewBag.Title

+ +
+
+
+
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ + @if (Model.Result != null) + { +
+ @if (Model.Result.Errors.Count == 0) + { +
+ + +
+ } + else + { + await Html.RenderPartialAsync("_JsEvaluationErrorList", Model.Result.Errors); + } +
+ } +
+
+
+ +@section Scripts { + + + + + + + + + +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Home/Index.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Home/Index.cshtml new file mode 100644 index 00000000..a9c2cfb1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = string.Empty; +} + +
+

Project Description

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Shared/Error.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Shared/Error.cshtml new file mode 100644 index 00000000..1010fafb --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Shared/Error.cshtml @@ -0,0 +1,25 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21.Models + +@model ErrorViewModel + +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +@if (Model.ShowRequestId) +{ +

+ Request ID: @Model.RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. +

\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Shared/_JsEvaluationErrorList.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Shared/_JsEvaluationErrorList.cshtml new file mode 100644 index 00000000..9e0601d6 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Shared/_JsEvaluationErrorList.cshtml @@ -0,0 +1,23 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models + +@model IList + +

Found @Model.Count error(s):

+
    + @foreach (var error in Model) + { +
  • + @error.EngineFullName
    + @if (error.LineNumber > 0) + { + Line @error.LineNumber, Column @error.ColumnNumber
    + } + @Html.EncodedReplace(@error.Message, "\n\r?", "
    ") + @if (!string.IsNullOrWhiteSpace(error.SourceFragment)) + { +
    @error.SourceFragment
    + } +
  • + } +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Shared/_Layout.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..93808b86 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/Shared/_Layout.cshtml @@ -0,0 +1,107 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers + + + + + + + + + @if (!string.IsNullOrEmpty(ViewBag.Title)) {<text>@ViewBag.Title | </text>}JavaScriptEngineSwitcher Sample ASP.NET Core 2.1 MVC 2.1 Site + + + + + + + + + + + + +
+ + + + + +
+
+ @RenderBody() +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @RenderSection("scripts", required: false) + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/_ViewImports.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/_ViewImports.cshtml new file mode 100644 index 00000000..8fe8384e --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" +@addTagHelper "*, JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure" \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/_ViewStart.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/_ViewStart.cshtml new file mode 100644 index 00000000..817a9134 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/appsettings.Development.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/appsettings.Development.json new file mode 100644 index 00000000..fa8ce71a --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/appsettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/appsettings.json new file mode 100644 index 00000000..7124dec4 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore21.Mvc21/appsettings.json @@ -0,0 +1,16 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + + "JsEngineSwitcher": { + "Samples": { + "TextContentDirectoryPath": "../SharedData/text-content" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Controllers/HomeController.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Controllers/HomeController.cs new file mode 100644 index 00000000..5922c698 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Controllers/HomeController.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; +using System.Threading.Tasks; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +using JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Services; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31.Controllers +{ + public class HomeController : Controller + { + private readonly FileContentService _fileContentService; + private readonly JsEvaluationService _jsEvaluationService; + + + public HomeController( + IConfigurationRoot configuration, + IWebHostEnvironment hostingEnvironment, + JsEvaluationService jsEvaluationService) + { + string textContentDirectoryPath = configuration + .GetSection("jsengineswitcher") + .GetSection("Samples")["TextContentDirectoryPath"] + ; + + _fileContentService = new FileContentService(textContentDirectoryPath, hostingEnvironment); + _jsEvaluationService = jsEvaluationService; + } + + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Index() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("index.html")); + + return View(); + } + + [HttpGet] + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Demo() + { + var model = _jsEvaluationService.GetInitializationData(); + + return View(model); + } + + [HttpPost] + public async Task Demo(JsEvaluationViewModel editedModel) + { + var model = _jsEvaluationService.GetInitializationData(); + await TryUpdateModelAsync(model, string.Empty, m => m.EngineName, m=> m.Expression); + + if (ModelState.IsValid) + { + model = _jsEvaluationService.Evaluate(model); + + ModelState.Clear(); + } + + return View(model); + } + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Contact() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("contact.html")); + + return View(); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31.csproj new file mode 100644 index 00000000..807b01b2 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31.csproj @@ -0,0 +1,50 @@ + + + + JS Engine Switcher: Sample ASP.NET Core 3.1 MVC 3.1 Site + 3.30.2 + netcoreapp3.1 + Exe + true + true + true + true + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Models/ErrorViewModel.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Models/ErrorViewModel.cs new file mode 100644 index 00000000..c2adb306 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Models/ErrorViewModel.cs @@ -0,0 +1,11 @@ +using System; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31.Models +{ + public class ErrorViewModel + { + public string RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Program.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Program.cs new file mode 100644 index 00000000..889d2a4b --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Program.cs @@ -0,0 +1,34 @@ +using System.IO; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31 +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseWebRoot(Path.Combine( + Directory.GetCurrentDirectory(), + "../JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot" + )); + webBuilder.UseStartup(); + }) + .ConfigureLogging((hostingContext, logging) => + { + logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); + logging.AddConsole(); + logging.AddDebug(); + }) + ; + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Properties/launchSettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Properties/launchSettings.json new file mode 100644 index 00000000..1b7533c8 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:55126", + "sslPort": 0 + } + }, + "profiles": { + "JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "applicationUrl": "http://localhost:5138", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Startup.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Startup.cs new file mode 100644 index 00000000..b90eb827 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Startup.cs @@ -0,0 +1,126 @@ +using Jering.Javascript.NodeJS; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.Node; +using JavaScriptEngineSwitcher.Sample.Logic.Services; +using JavaScriptEngineSwitcher.V8; +using JavaScriptEngineSwitcher.Vroom; +using JavaScriptEngineSwitcher.Yantra; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31 +{ + public class Startup + { + /// + /// Gets or sets a instance of hosting environment + /// + public IWebHostEnvironment HostingEnvironment + { + get; + set; + } + + public IConfigurationRoot Configuration + { + get; + set; + } + + + public Startup(IWebHostEnvironment env) + { + HostingEnvironment = env; + + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables(); + Configuration = builder.Build(); + } + + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(Configuration); + + // Add Jering Node.js service to the services container. + services.AddNodeJS(); + + // Add JavaScriptEngineSwitcher services to the services container. + services.AddJsEngineSwitcher(options => + { + options.AllowCurrentProperty = false; + options.DefaultEngineName = ChakraCoreJsEngine.EngineName; + }) + .AddChakraCore() + .AddJint() + .AddJurassic() + .AddMsie(options => + { + options.EngineMode = JsEngineMode.ChakraIeJsRt; + }) + .AddNiL() + .AddNode(services) + .AddV8() + .AddVroom() + .AddYantra() + ; + + services.Configure(options => + { + options.CacheProfiles.Add("CacheCompressedContent5Minutes", + new CacheProfile + { + NoStore = HostingEnvironment.IsDevelopment(), + Duration = 300, + Location = ResponseCacheLocation.Client, + VaryByHeader = "Accept-Encoding" + } + ); + }); + + // Add framework services. + services.AddControllersWithViews(); + + // Add JavaScriptEngineSwitcher sample services to the services container. + services.AddSingleton(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Home/Error"); + } + + app.UseStaticFiles(); + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Home/Contact.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Home/Contact.cshtml new file mode 100644 index 00000000..58785ebe --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Home/Contact.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Contact"; +} + +
+

@ViewBag.Title

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Home/Demo.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Home/Demo.cshtml new file mode 100644 index 00000000..befb8f43 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Home/Demo.cshtml @@ -0,0 +1,67 @@ +@using JavaScriptEngineSwitcher.Sample.Logic.Models +@using JavaScriptEngineSwitcher.Sample.Resources + +@model JsEvaluationViewModel + +@{ + ViewBag.Title = "Demo"; +} + +

@ViewBag.Title

+ +
+
+
+
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ + @if (Model.Result != null) + { +
+ @if (Model.Result.Errors.Count == 0) + { +
+ + +
+ } + else + { + await Html.RenderPartialAsync("_JsEvaluationErrorList", Model.Result.Errors); + } +
+ } +
+
+
+ +@section Scripts { + + + + + + + + + +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Home/Index.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Home/Index.cshtml new file mode 100644 index 00000000..a9c2cfb1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = string.Empty; +} + +
+

Project Description

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Shared/Error.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Shared/Error.cshtml new file mode 100644 index 00000000..4442769d --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Shared/Error.cshtml @@ -0,0 +1,28 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31.Models + +@model ErrorViewModel + +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +@if (Model.ShowRequestId) +{ +

+ Request ID: @Model.RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Shared/_JsEvaluationErrorList.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Shared/_JsEvaluationErrorList.cshtml new file mode 100644 index 00000000..9e0601d6 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Shared/_JsEvaluationErrorList.cshtml @@ -0,0 +1,23 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models + +@model IList + +

Found @Model.Count error(s):

+
    + @foreach (var error in Model) + { +
  • + @error.EngineFullName
    + @if (error.LineNumber > 0) + { + Line @error.LineNumber, Column @error.ColumnNumber
    + } + @Html.EncodedReplace(@error.Message, "\n\r?", "
    ") + @if (!string.IsNullOrWhiteSpace(error.SourceFragment)) + { +
    @error.SourceFragment
    + } +
  • + } +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Shared/_Layout.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..e9ffc46f --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/Shared/_Layout.cshtml @@ -0,0 +1,107 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers + + + + + + + + + @if (!string.IsNullOrEmpty(ViewBag.Title)) {<text>@ViewBag.Title | </text>}JavaScriptEngineSwitcher Sample ASP.NET Core 3.1 MVC 3.1 Site + + + + + + + + + + + + +
+ + + + + +
+
+ @RenderBody() +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @RenderSection("scripts", required: false) + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/_ViewImports.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/_ViewImports.cshtml new file mode 100644 index 00000000..8fe8384e --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" +@addTagHelper "*, JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure" \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/_ViewStart.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/_ViewStart.cshtml new file mode 100644 index 00000000..817a9134 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/appsettings.Development.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/appsettings.Development.json new file mode 100644 index 00000000..b10423ed --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/appsettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/appsettings.json new file mode 100644 index 00000000..81f55403 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore31.Mvc31/appsettings.json @@ -0,0 +1,17 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + + "JsEngineSwitcher": { + "Samples": { + "TextContentDirectoryPath": "../SharedData/text-content" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Controllers/HomeController.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Controllers/HomeController.cs new file mode 100644 index 00000000..30777c5d --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Controllers/HomeController.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; +using System.Threading.Tasks; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +using JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Services; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5.Controllers +{ + public class HomeController : Controller + { + private readonly FileContentService _fileContentService; + private readonly JsEvaluationService _jsEvaluationService; + + + public HomeController( + IConfigurationRoot configuration, + IWebHostEnvironment hostingEnvironment, + JsEvaluationService jsEvaluationService) + { + string textContentDirectoryPath = configuration + .GetSection("jsengineswitcher") + .GetSection("Samples")["TextContentDirectoryPath"] + ; + + _fileContentService = new FileContentService(textContentDirectoryPath, hostingEnvironment); + _jsEvaluationService = jsEvaluationService; + } + + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Index() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("index.html")); + + return View(); + } + + [HttpGet] + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Demo() + { + var model = _jsEvaluationService.GetInitializationData(); + + return View(model); + } + + [HttpPost] + public async Task Demo(JsEvaluationViewModel editedModel) + { + var model = _jsEvaluationService.GetInitializationData(); + await TryUpdateModelAsync(model, string.Empty, m => m.EngineName, m=> m.Expression); + + if (ModelState.IsValid) + { + model = _jsEvaluationService.Evaluate(model); + + ModelState.Clear(); + } + + return View(model); + } + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Contact() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("contact.html")); + + return View(); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5.csproj new file mode 100644 index 00000000..07820916 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5.csproj @@ -0,0 +1,50 @@ + + + + JS Engine Switcher: Sample ASP.NET Core 5.0 MVC 5 Site + 3.30.2 + net5.0 + Exe + true + true + true + true + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Models/ErrorViewModel.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Models/ErrorViewModel.cs new file mode 100644 index 00000000..81e5e7ae --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Models/ErrorViewModel.cs @@ -0,0 +1,11 @@ +using System; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5.Models +{ + public class ErrorViewModel + { + public string RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Program.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Program.cs new file mode 100644 index 00000000..376442fc --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Program.cs @@ -0,0 +1,34 @@ +using System.IO; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5 +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseWebRoot(Path.Combine( + Directory.GetCurrentDirectory(), + "../JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot" + )); + webBuilder.UseStartup(); + }) + .ConfigureLogging((hostingContext, logging) => + { + logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); + logging.AddConsole(); + logging.AddDebug(); + }) + ; + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Properties/launchSettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Properties/launchSettings.json new file mode 100644 index 00000000..7b2ce114 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:19282", + "sslPort": 0 + } + }, + "profiles": { + "JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "applicationUrl": "http://localhost:5121", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Startup.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Startup.cs new file mode 100644 index 00000000..2ef8be36 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Startup.cs @@ -0,0 +1,126 @@ +using Jering.Javascript.NodeJS; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.Node; +using JavaScriptEngineSwitcher.Sample.Logic.Services; +using JavaScriptEngineSwitcher.V8; +using JavaScriptEngineSwitcher.Vroom; +using JavaScriptEngineSwitcher.Yantra; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5 +{ + public class Startup + { + /// + /// Gets or sets a instance of hosting environment + /// + public IWebHostEnvironment HostingEnvironment + { + get; + set; + } + + public IConfigurationRoot Configuration + { + get; + set; + } + + + public Startup(IWebHostEnvironment env) + { + HostingEnvironment = env; + + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables(); + Configuration = builder.Build(); + } + + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(Configuration); + + // Add Jering Node.js service to the services container. + services.AddNodeJS(); + + // Add JavaScriptEngineSwitcher services to the services container. + services.AddJsEngineSwitcher(options => + { + options.AllowCurrentProperty = false; + options.DefaultEngineName = ChakraCoreJsEngine.EngineName; + }) + .AddChakraCore() + .AddJint() + .AddJurassic() + .AddMsie(options => + { + options.EngineMode = JsEngineMode.ChakraIeJsRt; + }) + .AddNiL() + .AddNode(services) + .AddV8() + .AddVroom() + .AddYantra() + ; + + services.Configure(options => + { + options.CacheProfiles.Add("CacheCompressedContent5Minutes", + new CacheProfile + { + NoStore = HostingEnvironment.IsDevelopment(), + Duration = 300, + Location = ResponseCacheLocation.Client, + VaryByHeader = "Accept-Encoding" + } + ); + }); + + // Add framework services. + services.AddControllersWithViews(); + + // Add JavaScriptEngineSwitcher sample services to the services container. + services.AddSingleton(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Home/Error"); + } + + app.UseStaticFiles(); + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Home/Contact.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Home/Contact.cshtml new file mode 100644 index 00000000..58785ebe --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Home/Contact.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Contact"; +} + +
+

@ViewBag.Title

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Home/Demo.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Home/Demo.cshtml new file mode 100644 index 00000000..befb8f43 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Home/Demo.cshtml @@ -0,0 +1,67 @@ +@using JavaScriptEngineSwitcher.Sample.Logic.Models +@using JavaScriptEngineSwitcher.Sample.Resources + +@model JsEvaluationViewModel + +@{ + ViewBag.Title = "Demo"; +} + +

@ViewBag.Title

+ +
+
+
+
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ + @if (Model.Result != null) + { +
+ @if (Model.Result.Errors.Count == 0) + { +
+ + +
+ } + else + { + await Html.RenderPartialAsync("_JsEvaluationErrorList", Model.Result.Errors); + } +
+ } +
+
+
+ +@section Scripts { + + + + + + + + + +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Home/Index.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Home/Index.cshtml new file mode 100644 index 00000000..a9c2cfb1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = string.Empty; +} + +
+

Project Description

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Shared/Error.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Shared/Error.cshtml new file mode 100644 index 00000000..667a018f --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Shared/Error.cshtml @@ -0,0 +1,28 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5.Models + +@model ErrorViewModel + +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +@if (Model.ShowRequestId) +{ +

+ Request ID: @Model.RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Shared/_JsEvaluationErrorList.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Shared/_JsEvaluationErrorList.cshtml new file mode 100644 index 00000000..9e0601d6 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Shared/_JsEvaluationErrorList.cshtml @@ -0,0 +1,23 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models + +@model IList + +

Found @Model.Count error(s):

+
    + @foreach (var error in Model) + { +
  • + @error.EngineFullName
    + @if (error.LineNumber > 0) + { + Line @error.LineNumber, Column @error.ColumnNumber
    + } + @Html.EncodedReplace(@error.Message, "\n\r?", "
    ") + @if (!string.IsNullOrWhiteSpace(error.SourceFragment)) + { +
    @error.SourceFragment
    + } +
  • + } +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Shared/_Layout.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..5fe54776 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/Shared/_Layout.cshtml @@ -0,0 +1,107 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers + + + + + + + + + @if (!string.IsNullOrEmpty(ViewBag.Title)) {<text>@ViewBag.Title | </text>}JavaScriptEngineSwitcher Sample ASP.NET Core 5.0 MVC 5 Site + + + + + + + + + + + + +
+ + + + + +
+
+ @RenderBody() +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @await RenderSectionAsync("scripts", required: false) + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/_ViewImports.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/_ViewImports.cshtml new file mode 100644 index 00000000..8fe8384e --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" +@addTagHelper "*, JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure" \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/_ViewStart.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/_ViewStart.cshtml new file mode 100644 index 00000000..817a9134 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/appsettings.Development.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/appsettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/appsettings.json new file mode 100644 index 00000000..85e9db19 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore5.Mvc5/appsettings.json @@ -0,0 +1,16 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + + "JsEngineSwitcher": { + "Samples": { + "TextContentDirectoryPath": "../SharedData/text-content" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Controllers/HomeController.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Controllers/HomeController.cs new file mode 100644 index 00000000..3c8faaeb --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Controllers/HomeController.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; +using System.Threading.Tasks; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +using JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Services; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6.Controllers +{ + public class HomeController : Controller + { + private readonly FileContentService _fileContentService; + private readonly JsEvaluationService _jsEvaluationService; + + + public HomeController( + IConfigurationRoot configuration, + IWebHostEnvironment hostingEnvironment, + JsEvaluationService jsEvaluationService) + { + string textContentDirectoryPath = configuration + .GetSection("jsengineswitcher") + .GetSection("Samples")["TextContentDirectoryPath"] + ; + + _fileContentService = new FileContentService(textContentDirectoryPath, hostingEnvironment); + _jsEvaluationService = jsEvaluationService; + } + + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Index() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("index.html")); + + return View(); + } + + [HttpGet] + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Demo() + { + var model = _jsEvaluationService.GetInitializationData(); + + return View(model); + } + + [HttpPost] + public async Task Demo(JsEvaluationViewModel editedModel) + { + var model = _jsEvaluationService.GetInitializationData(); + await TryUpdateModelAsync(model, string.Empty, m => m.EngineName, m=> m.Expression); + + if (ModelState.IsValid) + { + model = _jsEvaluationService.Evaluate(model); + + ModelState.Clear(); + } + + return View(model); + } + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Contact() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("contact.html")); + + return View(); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6.csproj new file mode 100644 index 00000000..726386ed --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6.csproj @@ -0,0 +1,51 @@ + + + + JS Engine Switcher: Sample ASP.NET Core 6.0 MVC 6 Site + 3.30.2 + net6.0 + enable + Exe + true + $(NoWarn);NETSDK1138 + true + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Models/ErrorViewModel.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Models/ErrorViewModel.cs new file mode 100644 index 00000000..8bd1a785 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Models/ErrorViewModel.cs @@ -0,0 +1,9 @@ +namespace JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6.Models +{ + public class ErrorViewModel + { + public string RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Program.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Program.cs new file mode 100644 index 00000000..2e472860 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Program.cs @@ -0,0 +1,106 @@ +using Jering.Javascript.NodeJS; +using Microsoft.AspNetCore.Mvc; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.Node; +using JavaScriptEngineSwitcher.Sample.Logic.Services; +using JavaScriptEngineSwitcher.V8; +using JavaScriptEngineSwitcher.Vroom; +using JavaScriptEngineSwitcher.Yantra; + +var builder = WebApplication.CreateBuilder(new WebApplicationOptions() +{ + WebRootPath = Path.Combine( + Directory.GetCurrentDirectory(), + "../JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot" + ) +}); +var env = builder.Environment; +var configuration = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables() + .Build() + ; + +#region Configure services + +IServiceCollection services = builder.Services; + +services.AddSingleton(configuration); + +// Add Jering Node.js service to the services container. +services.AddNodeJS(); + +// Add JavaScriptEngineSwitcher services to the services container. +services.AddJsEngineSwitcher(options => +{ + options.AllowCurrentProperty = false; + options.DefaultEngineName = ChakraCoreJsEngine.EngineName; +}) + .AddChakraCore() + .AddJint() + .AddJurassic() + .AddMsie(options => + { + options.EngineMode = JsEngineMode.ChakraIeJsRt; + }) + .AddNiL() + .AddNode(services) + .AddV8() + .AddVroom() + .AddYantra() + ; + +services.Configure(options => +{ + options.CacheProfiles.Add("CacheCompressedContent5Minutes", + new CacheProfile + { + NoStore = builder.Environment.IsDevelopment(), + Duration = 300, + Location = ResponseCacheLocation.Client, + VaryByHeader = "Accept-Encoding" + } + ); +}); + +// Add framework services. +services.AddControllersWithViews(); + +// Add JavaScriptEngineSwitcher sample services to the services container. +services.AddSingleton(); + +#endregion + +#region Configure the HTTP request pipeline + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} +else +{ + app.UseExceptionHandler("/Home/Error"); +} + +app.UseStaticFiles(); + +app.UseRouting(); + +app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}" +); + +#endregion + +app.Run(); \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Properties/launchSettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Properties/launchSettings.json new file mode 100644 index 00000000..3410f950 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:20428", + "sslPort": 0 + } + }, + "profiles": { + "JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5090", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Home/Contact.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Home/Contact.cshtml new file mode 100644 index 00000000..58785ebe --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Home/Contact.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Contact"; +} + +
+

@ViewBag.Title

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Home/Demo.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Home/Demo.cshtml new file mode 100644 index 00000000..befb8f43 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Home/Demo.cshtml @@ -0,0 +1,67 @@ +@using JavaScriptEngineSwitcher.Sample.Logic.Models +@using JavaScriptEngineSwitcher.Sample.Resources + +@model JsEvaluationViewModel + +@{ + ViewBag.Title = "Demo"; +} + +

@ViewBag.Title

+ +
+
+
+
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ + @if (Model.Result != null) + { +
+ @if (Model.Result.Errors.Count == 0) + { +
+ + +
+ } + else + { + await Html.RenderPartialAsync("_JsEvaluationErrorList", Model.Result.Errors); + } +
+ } +
+
+
+ +@section Scripts { + + + + + + + + + +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Home/Index.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Home/Index.cshtml new file mode 100644 index 00000000..a9c2cfb1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = string.Empty; +} + +
+

Project Description

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Shared/Error.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Shared/Error.cshtml new file mode 100644 index 00000000..4d1b91ca --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Shared/Error.cshtml @@ -0,0 +1,28 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6.Models + +@model ErrorViewModel + +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +@if (Model?.ShowRequestId ?? false) +{ +

+ Request ID: @Model?.RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Shared/_JsEvaluationErrorList.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Shared/_JsEvaluationErrorList.cshtml new file mode 100644 index 00000000..9e0601d6 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Shared/_JsEvaluationErrorList.cshtml @@ -0,0 +1,23 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models + +@model IList + +

Found @Model.Count error(s):

+
    + @foreach (var error in Model) + { +
  • + @error.EngineFullName
    + @if (error.LineNumber > 0) + { + Line @error.LineNumber, Column @error.ColumnNumber
    + } + @Html.EncodedReplace(@error.Message, "\n\r?", "
    ") + @if (!string.IsNullOrWhiteSpace(error.SourceFragment)) + { +
    @error.SourceFragment
    + } +
  • + } +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Shared/_Layout.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..b1f433fd --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/Shared/_Layout.cshtml @@ -0,0 +1,107 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers + + + + + + + + + @if (!string.IsNullOrEmpty(ViewBag.Title)) {<text>@ViewBag.Title | </text>}JavaScriptEngineSwitcher Sample ASP.NET Core 6.0 MVC 6 Site + + + + + + + + + + + + +
+ + + + + +
+
+ @RenderBody() +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @await RenderSectionAsync("scripts", required: false) + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/_ViewImports.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/_ViewImports.cshtml new file mode 100644 index 00000000..8fe8384e --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" +@addTagHelper "*, JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure" \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/_ViewStart.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/_ViewStart.cshtml new file mode 100644 index 00000000..817a9134 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/appsettings.Development.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/appsettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/appsettings.json new file mode 100644 index 00000000..ed44b7ae --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore6.Mvc6/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + + "JsEngineSwitcher": { + "Samples": { + "TextContentDirectoryPath": "../SharedData/text-content" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Controllers/HomeController.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Controllers/HomeController.cs new file mode 100644 index 00000000..6f6ed28e --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Controllers/HomeController.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; +using System.Threading.Tasks; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +using JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Services; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7.Controllers +{ + public class HomeController : Controller + { + private readonly FileContentService _fileContentService; + private readonly JsEvaluationService _jsEvaluationService; + + + public HomeController( + IConfigurationRoot configuration, + IWebHostEnvironment hostingEnvironment, + JsEvaluationService jsEvaluationService) + { + string textContentDirectoryPath = configuration + .GetSection("jsengineswitcher") + .GetSection("Samples")["TextContentDirectoryPath"] + ; + + _fileContentService = new FileContentService(textContentDirectoryPath, hostingEnvironment); + _jsEvaluationService = jsEvaluationService; + } + + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Index() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("index.html")); + + return View(); + } + + [HttpGet] + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Demo() + { + var model = _jsEvaluationService.GetInitializationData(); + + return View(model); + } + + [HttpPost] + public async Task Demo(JsEvaluationViewModel editedModel) + { + var model = _jsEvaluationService.GetInitializationData(); + await TryUpdateModelAsync(model, string.Empty, m => m.EngineName, m=> m.Expression); + + if (ModelState.IsValid) + { + model = _jsEvaluationService.Evaluate(model); + + ModelState.Clear(); + } + + return View(model); + } + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Contact() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("contact.html")); + + return View(); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7.csproj new file mode 100644 index 00000000..a2750c47 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7.csproj @@ -0,0 +1,51 @@ + + + + JS Engine Switcher: Sample ASP.NET Core 7.0 MVC 7 Site + 3.30.2 + net7.0 + enable + Exe + true + true + true + true + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Models/ErrorViewModel.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Models/ErrorViewModel.cs new file mode 100644 index 00000000..cf622ba3 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Models/ErrorViewModel.cs @@ -0,0 +1,9 @@ +namespace JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7.Models +{ + public class ErrorViewModel + { + public string RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Program.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Program.cs new file mode 100644 index 00000000..2e472860 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Program.cs @@ -0,0 +1,106 @@ +using Jering.Javascript.NodeJS; +using Microsoft.AspNetCore.Mvc; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.Node; +using JavaScriptEngineSwitcher.Sample.Logic.Services; +using JavaScriptEngineSwitcher.V8; +using JavaScriptEngineSwitcher.Vroom; +using JavaScriptEngineSwitcher.Yantra; + +var builder = WebApplication.CreateBuilder(new WebApplicationOptions() +{ + WebRootPath = Path.Combine( + Directory.GetCurrentDirectory(), + "../JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot" + ) +}); +var env = builder.Environment; +var configuration = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables() + .Build() + ; + +#region Configure services + +IServiceCollection services = builder.Services; + +services.AddSingleton(configuration); + +// Add Jering Node.js service to the services container. +services.AddNodeJS(); + +// Add JavaScriptEngineSwitcher services to the services container. +services.AddJsEngineSwitcher(options => +{ + options.AllowCurrentProperty = false; + options.DefaultEngineName = ChakraCoreJsEngine.EngineName; +}) + .AddChakraCore() + .AddJint() + .AddJurassic() + .AddMsie(options => + { + options.EngineMode = JsEngineMode.ChakraIeJsRt; + }) + .AddNiL() + .AddNode(services) + .AddV8() + .AddVroom() + .AddYantra() + ; + +services.Configure(options => +{ + options.CacheProfiles.Add("CacheCompressedContent5Minutes", + new CacheProfile + { + NoStore = builder.Environment.IsDevelopment(), + Duration = 300, + Location = ResponseCacheLocation.Client, + VaryByHeader = "Accept-Encoding" + } + ); +}); + +// Add framework services. +services.AddControllersWithViews(); + +// Add JavaScriptEngineSwitcher sample services to the services container. +services.AddSingleton(); + +#endregion + +#region Configure the HTTP request pipeline + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} +else +{ + app.UseExceptionHandler("/Home/Error"); +} + +app.UseStaticFiles(); + +app.UseRouting(); + +app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}" +); + +#endregion + +app.Run(); \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Properties/launchSettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Properties/launchSettings.json new file mode 100644 index 00000000..57ebee9a --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:32819", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5107", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Home/Contact.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Home/Contact.cshtml new file mode 100644 index 00000000..58785ebe --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Home/Contact.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Contact"; +} + +
+

@ViewBag.Title

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Home/Demo.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Home/Demo.cshtml new file mode 100644 index 00000000..befb8f43 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Home/Demo.cshtml @@ -0,0 +1,67 @@ +@using JavaScriptEngineSwitcher.Sample.Logic.Models +@using JavaScriptEngineSwitcher.Sample.Resources + +@model JsEvaluationViewModel + +@{ + ViewBag.Title = "Demo"; +} + +

@ViewBag.Title

+ +
+
+
+
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ + @if (Model.Result != null) + { +
+ @if (Model.Result.Errors.Count == 0) + { +
+ + +
+ } + else + { + await Html.RenderPartialAsync("_JsEvaluationErrorList", Model.Result.Errors); + } +
+ } +
+
+
+ +@section Scripts { + + + + + + + + + +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Home/Index.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Home/Index.cshtml new file mode 100644 index 00000000..a9c2cfb1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = string.Empty; +} + +
+

Project Description

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Shared/Error.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Shared/Error.cshtml new file mode 100644 index 00000000..1278d006 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Shared/Error.cshtml @@ -0,0 +1,28 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7.Models + +@model ErrorViewModel + +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +@if (Model.ShowRequestId) +{ +

+ Request ID: @Model.RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Shared/_JsEvaluationErrorList.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Shared/_JsEvaluationErrorList.cshtml new file mode 100644 index 00000000..9e0601d6 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Shared/_JsEvaluationErrorList.cshtml @@ -0,0 +1,23 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models + +@model IList + +

Found @Model.Count error(s):

+
    + @foreach (var error in Model) + { +
  • + @error.EngineFullName
    + @if (error.LineNumber > 0) + { + Line @error.LineNumber, Column @error.ColumnNumber
    + } + @Html.EncodedReplace(@error.Message, "\n\r?", "
    ") + @if (!string.IsNullOrWhiteSpace(error.SourceFragment)) + { +
    @error.SourceFragment
    + } +
  • + } +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Shared/_Layout.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..fe014242 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/Shared/_Layout.cshtml @@ -0,0 +1,107 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers + + + + + + + + + @if (!string.IsNullOrEmpty(ViewBag.Title)) {<text>@ViewBag.Title | </text>}JavaScriptEngineSwitcher Sample ASP.NET Core 7.0 MVC 7 Site + + + + + + + + + + + + +
+ + + + + +
+
+ @RenderBody() +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @await RenderSectionAsync("scripts", required: false) + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/_ViewImports.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/_ViewImports.cshtml new file mode 100644 index 00000000..8fe8384e --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" +@addTagHelper "*, JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure" \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/_ViewStart.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/_ViewStart.cshtml new file mode 100644 index 00000000..817a9134 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/appsettings.Development.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/appsettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/appsettings.json new file mode 100644 index 00000000..ed44b7ae --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore7.Mvc7/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + + "JsEngineSwitcher": { + "Samples": { + "TextContentDirectoryPath": "../SharedData/text-content" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Controllers/HomeController.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Controllers/HomeController.cs new file mode 100644 index 00000000..0128dc52 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Controllers/HomeController.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; +using System.Threading.Tasks; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +using JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Services; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8.Controllers +{ + public class HomeController : Controller + { + private readonly FileContentService _fileContentService; + private readonly JsEvaluationService _jsEvaluationService; + + + public HomeController( + IConfigurationRoot configuration, + IWebHostEnvironment hostingEnvironment, + JsEvaluationService jsEvaluationService) + { + string textContentDirectoryPath = configuration + .GetSection("jsengineswitcher") + .GetSection("Samples")["TextContentDirectoryPath"] + ; + + _fileContentService = new FileContentService(textContentDirectoryPath, hostingEnvironment); + _jsEvaluationService = jsEvaluationService; + } + + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Index() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("index.html")); + + return View(); + } + + [HttpGet] + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Demo() + { + var model = _jsEvaluationService.GetInitializationData(); + + return View(model); + } + + [HttpPost] + public async Task Demo(JsEvaluationViewModel editedModel) + { + var model = _jsEvaluationService.GetInitializationData(); + await TryUpdateModelAsync(model, string.Empty, m => m.EngineName, m=> m.Expression); + + if (ModelState.IsValid) + { + model = _jsEvaluationService.Evaluate(model); + + ModelState.Clear(); + } + + return View(model); + } + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Contact() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("contact.html")); + + return View(); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8.csproj new file mode 100644 index 00000000..1845c729 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8.csproj @@ -0,0 +1,49 @@ + + + + JS Engine Switcher: Sample ASP.NET Core 8.0 MVC 8 Site + 3.30.2 + net8.0 + enable + Exe + true + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Models/ErrorViewModel.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Models/ErrorViewModel.cs new file mode 100644 index 00000000..8cf789c5 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Models/ErrorViewModel.cs @@ -0,0 +1,9 @@ +namespace JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8.Models +{ + public class ErrorViewModel + { + public string RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Program.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Program.cs new file mode 100644 index 00000000..2e472860 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Program.cs @@ -0,0 +1,106 @@ +using Jering.Javascript.NodeJS; +using Microsoft.AspNetCore.Mvc; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.Node; +using JavaScriptEngineSwitcher.Sample.Logic.Services; +using JavaScriptEngineSwitcher.V8; +using JavaScriptEngineSwitcher.Vroom; +using JavaScriptEngineSwitcher.Yantra; + +var builder = WebApplication.CreateBuilder(new WebApplicationOptions() +{ + WebRootPath = Path.Combine( + Directory.GetCurrentDirectory(), + "../JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot" + ) +}); +var env = builder.Environment; +var configuration = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables() + .Build() + ; + +#region Configure services + +IServiceCollection services = builder.Services; + +services.AddSingleton(configuration); + +// Add Jering Node.js service to the services container. +services.AddNodeJS(); + +// Add JavaScriptEngineSwitcher services to the services container. +services.AddJsEngineSwitcher(options => +{ + options.AllowCurrentProperty = false; + options.DefaultEngineName = ChakraCoreJsEngine.EngineName; +}) + .AddChakraCore() + .AddJint() + .AddJurassic() + .AddMsie(options => + { + options.EngineMode = JsEngineMode.ChakraIeJsRt; + }) + .AddNiL() + .AddNode(services) + .AddV8() + .AddVroom() + .AddYantra() + ; + +services.Configure(options => +{ + options.CacheProfiles.Add("CacheCompressedContent5Minutes", + new CacheProfile + { + NoStore = builder.Environment.IsDevelopment(), + Duration = 300, + Location = ResponseCacheLocation.Client, + VaryByHeader = "Accept-Encoding" + } + ); +}); + +// Add framework services. +services.AddControllersWithViews(); + +// Add JavaScriptEngineSwitcher sample services to the services container. +services.AddSingleton(); + +#endregion + +#region Configure the HTTP request pipeline + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} +else +{ + app.UseExceptionHandler("/Home/Error"); +} + +app.UseStaticFiles(); + +app.UseRouting(); + +app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}" +); + +#endregion + +app.Run(); \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Properties/launchSettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Properties/launchSettings.json new file mode 100644 index 00000000..dda1c460 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:29737", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5282", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Home/Contact.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Home/Contact.cshtml new file mode 100644 index 00000000..58785ebe --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Home/Contact.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Contact"; +} + +
+

@ViewBag.Title

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Home/Demo.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Home/Demo.cshtml new file mode 100644 index 00000000..befb8f43 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Home/Demo.cshtml @@ -0,0 +1,67 @@ +@using JavaScriptEngineSwitcher.Sample.Logic.Models +@using JavaScriptEngineSwitcher.Sample.Resources + +@model JsEvaluationViewModel + +@{ + ViewBag.Title = "Demo"; +} + +

@ViewBag.Title

+ +
+
+
+
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ + @if (Model.Result != null) + { +
+ @if (Model.Result.Errors.Count == 0) + { +
+ + +
+ } + else + { + await Html.RenderPartialAsync("_JsEvaluationErrorList", Model.Result.Errors); + } +
+ } +
+
+
+ +@section Scripts { + + + + + + + + + +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Home/Index.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Home/Index.cshtml new file mode 100644 index 00000000..a9c2cfb1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = string.Empty; +} + +
+

Project Description

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Shared/Error.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Shared/Error.cshtml new file mode 100644 index 00000000..bfb58107 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Shared/Error.cshtml @@ -0,0 +1,28 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8.Models + +@model ErrorViewModel + +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +@if (Model.ShowRequestId) +{ +

+ Request ID: @Model.RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Shared/_JsEvaluationErrorList.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Shared/_JsEvaluationErrorList.cshtml new file mode 100644 index 00000000..9e0601d6 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Shared/_JsEvaluationErrorList.cshtml @@ -0,0 +1,23 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models + +@model IList + +

Found @Model.Count error(s):

+
    + @foreach (var error in Model) + { +
  • + @error.EngineFullName
    + @if (error.LineNumber > 0) + { + Line @error.LineNumber, Column @error.ColumnNumber
    + } + @Html.EncodedReplace(@error.Message, "\n\r?", "
    ") + @if (!string.IsNullOrWhiteSpace(error.SourceFragment)) + { +
    @error.SourceFragment
    + } +
  • + } +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Shared/_Layout.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..ebe02bba --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/Shared/_Layout.cshtml @@ -0,0 +1,107 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers + + + + + + + + + @if (!string.IsNullOrEmpty(ViewBag.Title)) {<text>@ViewBag.Title | </text>}JavaScriptEngineSwitcher Sample ASP.NET Core 8.0 MVC 8 Site + + + + + + + + + + + + +
+ + + + + +
+
+ @RenderBody() +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @await RenderSectionAsync("scripts", required: false) + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/_ViewImports.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/_ViewImports.cshtml new file mode 100644 index 00000000..8fe8384e --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" +@addTagHelper "*, JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure" \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/_ViewStart.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/_ViewStart.cshtml new file mode 100644 index 00000000..817a9134 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/appsettings.Development.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/appsettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/appsettings.json new file mode 100644 index 00000000..ed44b7ae --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore8.Mvc8/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + + "JsEngineSwitcher": { + "Samples": { + "TextContentDirectoryPath": "../SharedData/text-content" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Controllers/HomeController.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Controllers/HomeController.cs new file mode 100644 index 00000000..57f376ff --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Controllers/HomeController.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; +using System.Threading.Tasks; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +using JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Models; +using JavaScriptEngineSwitcher.Sample.Logic.Services; + +namespace JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9.Controllers +{ + public class HomeController : Controller + { + private readonly FileContentService _fileContentService; + private readonly JsEvaluationService _jsEvaluationService; + + + public HomeController( + IConfigurationRoot configuration, + IWebHostEnvironment hostingEnvironment, + JsEvaluationService jsEvaluationService) + { + string textContentDirectoryPath = configuration + .GetSection("jsengineswitcher") + .GetSection("Samples")["TextContentDirectoryPath"] + ; + + _fileContentService = new FileContentService(textContentDirectoryPath, hostingEnvironment); + _jsEvaluationService = jsEvaluationService; + } + + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Index() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("index.html")); + + return View(); + } + + [HttpGet] + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Demo() + { + var model = _jsEvaluationService.GetInitializationData(); + + return View(model); + } + + [HttpPost] + public async Task Demo(JsEvaluationViewModel editedModel) + { + var model = _jsEvaluationService.GetInitializationData(); + await TryUpdateModelAsync(model, string.Empty, m => m.EngineName, m=> m.Expression); + + if (ModelState.IsValid) + { + model = _jsEvaluationService.Evaluate(model); + + ModelState.Clear(); + } + + return View(model); + } + + [ResponseCache(CacheProfileName = "CacheCompressedContent5Minutes")] + public IActionResult Contact() + { + ViewBag.Body = new HtmlString(_fileContentService.GetFileContent("contact.html")); + + return View(); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9.csproj b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9.csproj new file mode 100644 index 00000000..8f79d123 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9.csproj @@ -0,0 +1,49 @@ + + + + JS Engine Switcher: Sample ASP.NET Core 9.0 MVC 9 Site + 3.30.2 + net9.0 + enable + Exe + true + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Models/ErrorViewModel.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Models/ErrorViewModel.cs new file mode 100644 index 00000000..20cc9827 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Models/ErrorViewModel.cs @@ -0,0 +1,9 @@ +namespace JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9.Models +{ + public class ErrorViewModel + { + public string RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Program.cs b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Program.cs new file mode 100644 index 00000000..457ccf63 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Program.cs @@ -0,0 +1,105 @@ +using Jering.Javascript.NodeJS; +using Microsoft.AspNetCore.Mvc; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.Node; +using JavaScriptEngineSwitcher.Sample.Logic.Services; +using JavaScriptEngineSwitcher.V8; +using JavaScriptEngineSwitcher.Vroom; +using JavaScriptEngineSwitcher.Yantra; + +var builder = WebApplication.CreateBuilder(new WebApplicationOptions() { + WebRootPath = Path.Combine( + Directory.GetCurrentDirectory(), + "../JavaScriptEngineSwitcher.Sample.AspNetCore.ClientSideAssets/wwwroot" + ) +}); +var env = builder.Environment; +var configuration = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables() + .Build() + ; + +#region Configure services + +IServiceCollection services = builder.Services; + +services.AddSingleton(configuration); + +// Add Jering Node.js service to the services container. +services.AddNodeJS(); + +// Add JavaScriptEngineSwitcher services to the services container. +services.AddJsEngineSwitcher(options => +{ + options.AllowCurrentProperty = false; + options.DefaultEngineName = ChakraCoreJsEngine.EngineName; +}) + .AddChakraCore() + .AddJint() + .AddJurassic() + .AddMsie(options => + { + options.EngineMode = JsEngineMode.ChakraIeJsRt; + }) + .AddNiL() + .AddNode(services) + .AddV8() + .AddVroom() + .AddYantra() + ; + +services.Configure(options => +{ + options.CacheProfiles.Add("CacheCompressedContent5Minutes", + new CacheProfile + { + NoStore = builder.Environment.IsDevelopment(), + Duration = 300, + Location = ResponseCacheLocation.Client, + VaryByHeader = "Accept-Encoding" + } + ); +}); + +// Add framework services. +services.AddControllersWithViews(); + +// Add JavaScriptEngineSwitcher sample services to the services container. +services.AddSingleton(); + +#endregion + +#region Configure the HTTP request pipeline + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} +else +{ + app.UseExceptionHandler("/Home/Error"); +} + +app.UseRouting(); + +app.MapStaticAssets(); + +app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}") + .WithStaticAssets(); + +#endregion + +app.Run(); \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Properties/launchSettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Properties/launchSettings.json new file mode 100644 index 00000000..a94e92ed --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Properties/launchSettings.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:53687/", + "sslPort": 44308 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5220" + }, + "https": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7242;http://localhost:5220" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Home/Contact.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Home/Contact.cshtml new file mode 100644 index 00000000..58785ebe --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Home/Contact.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Contact"; +} + +
+

@ViewBag.Title

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Home/Demo.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Home/Demo.cshtml new file mode 100644 index 00000000..befb8f43 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Home/Demo.cshtml @@ -0,0 +1,67 @@ +@using JavaScriptEngineSwitcher.Sample.Logic.Models +@using JavaScriptEngineSwitcher.Sample.Resources + +@model JsEvaluationViewModel + +@{ + ViewBag.Title = "Demo"; +} + +

@ViewBag.Title

+ +
+
+
+
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ + @if (Model.Result != null) + { +
+ @if (Model.Result.Errors.Count == 0) + { +
+ + +
+ } + else + { + await Html.RenderPartialAsync("_JsEvaluationErrorList", Model.Result.Errors); + } +
+ } +
+
+
+ +@section Scripts { + + + + + + + + + +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Home/Index.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Home/Index.cshtml new file mode 100644 index 00000000..a9c2cfb1 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = string.Empty; +} + +
+

Project Description

+ @ViewBag.Body +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Shared/Error.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Shared/Error.cshtml new file mode 100644 index 00000000..b568b9f2 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Shared/Error.cshtml @@ -0,0 +1,28 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9.Models + +@model ErrorViewModel + +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +@if (Model.ShowRequestId) +{ +

+ Request ID: @Model.RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Shared/_JsEvaluationErrorList.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Shared/_JsEvaluationErrorList.cshtml new file mode 100644 index 00000000..9e0601d6 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Shared/_JsEvaluationErrorList.cshtml @@ -0,0 +1,23 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.Helpers +@using JavaScriptEngineSwitcher.Sample.Logic.Models + +@model IList + +

Found @Model.Count error(s):

+
    + @foreach (var error in Model) + { +
  • + @error.EngineFullName
    + @if (error.LineNumber > 0) + { + Line @error.LineNumber, Column @error.ColumnNumber
    + } + @Html.EncodedReplace(@error.Message, "\n\r?", "
    ") + @if (!string.IsNullOrWhiteSpace(error.SourceFragment)) + { +
    @error.SourceFragment
    + } +
  • + } +
\ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Shared/_Layout.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..b595497f --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/Shared/_Layout.cshtml @@ -0,0 +1,107 @@ +@using JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure.TagHelpers + + + + + + + + + @if (!string.IsNullOrEmpty(ViewBag.Title)) {<text>@ViewBag.Title | </text>}JavaScriptEngineSwitcher Sample ASP.NET Core 9.0 MVC 9 Site + + + + + + + + + + + + +
+ + + + + +
+
+ @RenderBody() +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @await RenderSectionAsync("scripts", required: false) + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/_ViewImports.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/_ViewImports.cshtml new file mode 100644 index 00000000..dcad619c --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, JavaScriptEngineSwitcher.Sample.AspNetCore.Infrastructure \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/_ViewStart.cshtml b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/_ViewStart.cshtml new file mode 100644 index 00000000..817a9134 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/appsettings.Development.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/appsettings.json b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/appsettings.json new file mode 100644 index 00000000..ed44b7ae --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.AspNetCore9.Mvc9/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + + "JsEngineSwitcher": { + "Samples": { + "TextContentDirectoryPath": "../SharedData/text-content" + } + } +} diff --git a/samples/JavaScriptEngineSwitcher.Sample.Logic/JavaScriptEngineSwitcher.Sample.Logic.csproj b/samples/JavaScriptEngineSwitcher.Sample.Logic/JavaScriptEngineSwitcher.Sample.Logic.csproj new file mode 100644 index 00000000..9112f74a --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.Logic/JavaScriptEngineSwitcher.Sample.Logic.csproj @@ -0,0 +1,68 @@ + + + + JS Engine Switcher: Logic for Samples + 3.30.2 + net40;net451;net471;netstandard1.6;netstandard2.0;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0 + 1.6.0 + Library + true + $(NoWarn);NETSDK1215;NU1902;NU1903;NU1904 + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.Logic/Models/JsEvaluationErrorViewModel.cs b/samples/JavaScriptEngineSwitcher.Sample.Logic/Models/JsEvaluationErrorViewModel.cs new file mode 100644 index 00000000..98f627e2 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.Logic/Models/JsEvaluationErrorViewModel.cs @@ -0,0 +1,98 @@ +namespace JavaScriptEngineSwitcher.Sample.Logic.Models +{ + /// + /// JS evaluation error view model + /// + public sealed class JsEvaluationErrorViewModel + { + /// + /// Gets or sets a name of JS engine + /// + public string EngineName + { + get; + set; + } + + /// + /// Gets or sets a version of original JS engine + /// + public string EngineVersion + { + get; + set; + } + + /// + /// Gets a full name of JS engine + /// + public string EngineFullName + { + get + { + string engineFullName = EngineName + " "; + if (EngineVersion.Contains(".") || EngineVersion.Contains(",")) + { + engineFullName += "version "; + if (EngineVersion.Contains(",")) + { + engineFullName += "of "; + } + } + engineFullName += EngineVersion; + + return engineFullName; + } + } + + /// + /// Gets or sets a message + /// + public string Message + { + get; + set; + } + + /// + /// Gets or sets a line number + /// + public int LineNumber + { + get; + set; + } + + /// + /// Gets or sets a column number + /// + public int ColumnNumber + { + get; + set; + } + + /// + /// Gets or sets a source fragment + /// + public string SourceFragment + { + get; + set; + } + + + /// + /// Constructs an instance of JS evaluation error view model + /// + public JsEvaluationErrorViewModel() + { + EngineName = string.Empty; + EngineVersion = string.Empty; + Message = string.Empty; + LineNumber = 0; + ColumnNumber = 0; + SourceFragment = string.Empty; + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.Logic/Models/JsEvaluationResultViewModel.cs b/samples/JavaScriptEngineSwitcher.Sample.Logic/Models/JsEvaluationResultViewModel.cs new file mode 100644 index 00000000..5ffc63ca --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.Logic/Models/JsEvaluationResultViewModel.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +using JavaScriptEngineSwitcher.Sample.Resources; + +namespace JavaScriptEngineSwitcher.Sample.Logic.Models +{ + /// + /// JS evaluation result view model + /// + public sealed class JsEvaluationResultViewModel + { + /// + /// Gets or sets a result value + /// + [Display(Name = "DisplayName_ResultValue", ResourceType = typeof(EvaluationStrings))] + [DataType(DataType.MultilineText)] + public string Value + { + get; + set; + } + + /// + /// Gets or sets a list of errors + /// + public IList Errors + { + get; + set; + } + + + /// + /// Constructs an instance of JS evaluation result view model + /// + public JsEvaluationResultViewModel() + { + Value = string.Empty; + Errors = new List(); + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.Logic/Models/JsEvaluationViewModel.cs b/samples/JavaScriptEngineSwitcher.Sample.Logic/Models/JsEvaluationViewModel.cs new file mode 100644 index 00000000..0cca3971 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.Logic/Models/JsEvaluationViewModel.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +#if NET451_OR_GREATER || NETSTANDARD || NETCOREAPP +using Microsoft.AspNetCore.Mvc.Rendering; +#elif NET40 +using System.Web.Mvc; +#else +#error No implementation for this target +#endif + +using JavaScriptEngineSwitcher.Sample.Resources; + +namespace JavaScriptEngineSwitcher.Sample.Logic.Models +{ + /// + /// JS evaluation view model + /// + public sealed class JsEvaluationViewModel + { + /// + /// Gets or sets a name of JS engine + /// + [Display(Name = "DisplayName_EngineName", ResourceType = typeof(EvaluationStrings))] + public string EngineName + { + get; + set; + } + + /// + /// Gets or sets a list of available JS engines + /// + public IEnumerable AvailableEngineList + { + get; + set; + } + + /// + /// Gets or sets a expression + /// + [Display(Name = "DisplayName_Expression", ResourceType = typeof(EvaluationStrings))] + [DataType(DataType.MultilineText)] + [Required(ErrorMessageResourceName = "ErrorMessage_FormFieldIsNotFilled", ErrorMessageResourceType = typeof(EvaluationStrings))] + [StringLength(1000000, ErrorMessageResourceName = "ErrorMessage_FormFieldValueTooLong", ErrorMessageResourceType = typeof(EvaluationStrings))] + public string Expression + { + get; + set; + } + + /// + /// Gets or sets a result of evaluation + /// + public JsEvaluationResultViewModel Result + { + get; + set; + } + + + /// + /// Constructs an instance of JS evaluation view model + /// + public JsEvaluationViewModel() + { + EngineName = string.Empty; + AvailableEngineList = new List(); + Expression = string.Empty; + Result = null; + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.Logic/Services/FileContentService.cs b/samples/JavaScriptEngineSwitcher.Sample.Logic/Services/FileContentService.cs new file mode 100644 index 00000000..de909780 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.Logic/Services/FileContentService.cs @@ -0,0 +1,97 @@ +using System; +using System.IO; +#if NET451_OR_GREATER || NETSTANDARD +using HostingEnvironment = Microsoft.AspNetCore.Hosting.IHostingEnvironment; +#elif NETCOREAPP3_1_OR_GREATER +using HostingEnvironment = Microsoft.AspNetCore.Hosting.IWebHostEnvironment; +#elif NET40 +using System.Web; +#else +#error No implementation for this target +#endif + +using JavaScriptEngineSwitcher.Sample.Resources; + +namespace JavaScriptEngineSwitcher.Sample.Logic.Services +{ + public sealed class FileContentService + { + private readonly string _textContentDirectoryPath; +#if NET451_OR_GREATER || NETSTANDARD || NETCOREAPP + private readonly HostingEnvironment _hostingEnvironment; +#endif + + +#if NET451_OR_GREATER || NETSTANDARD || NETCOREAPP + public FileContentService( + string textContentDirectoryPath, + HostingEnvironment hostingEnvironment + ) + { + _textContentDirectoryPath = textContentDirectoryPath; + _hostingEnvironment = hostingEnvironment; + } +#elif NET40 + public FileContentService(string textContentDirectoryPath) + { + _textContentDirectoryPath = textContentDirectoryPath; + } +#else +#error No implementation for this target +#endif + + public string GetFileContent(string filePath) + { + if (string.IsNullOrWhiteSpace(filePath)) + { + throw new ArgumentException( + string.Format(CommonStrings.ErrorMessage_FilePathNotSpecified, filePath), + "filePath" + ); + } + + string content; + string fullFilePath = _textContentDirectoryPath.TrimEnd('/') + "/" + filePath; + string physicalFilePath = GetPhysicalFilePath(fullFilePath); + + try + { + using (FileStream fileStream = File.OpenRead(physicalFilePath)) + using (var reader = new StreamReader(fileStream)) + { + content = reader.ReadToEnd(); + } + } + catch (FileNotFoundException) + { + throw new FileNotFoundException( + string.Format(CommonStrings.ErrorMessage_FileNotFound, filePath)); + } + catch (DirectoryNotFoundException) + { + throw new FileNotFoundException( + string.Format(CommonStrings.ErrorMessage_FileNotFound, filePath)); + } + + return content; + } + + private string GetPhysicalFilePath(string filePath) + { +#if NET451_OR_GREATER || NETSTANDARD || NETCOREAPP + string applicationDirectoryPath = _hostingEnvironment.ContentRootPath; +#elif NET40 + HttpContext context = HttpContext.Current; + string applicationDirectoryPath = context.Server.MapPath("~/"); +#else +#error No implementation for this target +#endif + string physicalFilePath = Path.Combine( + applicationDirectoryPath, + filePath.Replace('/', '\\').TrimStart(new char[] { '\\' }) + ); + + return physicalFilePath; + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.Logic/Services/JsEvaluationService.cs b/samples/JavaScriptEngineSwitcher.Sample.Logic/Services/JsEvaluationService.cs new file mode 100644 index 00000000..2317ac51 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.Logic/Services/JsEvaluationService.cs @@ -0,0 +1,135 @@ +using System.Collections.Generic; +using System.Linq; +#if NET451_OR_GREATER || NETSTANDARD || NETCOREAPP +using Microsoft.AspNetCore.Mvc.Rendering; +#elif NET40 +using System.Web.Mvc; +#else +#error No implementation for this target +#endif + +using JavaScriptEngineSwitcher.Core; + +using JavaScriptEngineSwitcher.Sample.Logic.Models; + +namespace JavaScriptEngineSwitcher.Sample.Logic.Services +{ + public class JsEvaluationService + { + private static readonly Dictionary _engineDisplayNameMappings; + + private readonly IJsEngineSwitcher _engineSwitcher; + + + static JsEvaluationService() + { + _engineDisplayNameMappings = new Dictionary + { + { "MsieJsEngine", "MSIE" } + }; + } + +#if NET40 + public JsEvaluationService() + : this(JsEngineSwitcher.Current) + { } + +#endif + public JsEvaluationService(IJsEngineSwitcher engineSwitcher) + { + _engineSwitcher = engineSwitcher; + } + + + public JsEvaluationViewModel GetInitializationData() + { + var model = new JsEvaluationViewModel + { + EngineName = _engineSwitcher.DefaultEngineName, + AvailableEngineList = _engineSwitcher.EngineFactories + .Select(e => new SelectListItem + { + Value = e.EngineName, + Text = GetEngineDisplayName(e.EngineName) + }), + Expression = string.Empty, + Result = null + }; + + return model; + } + + public JsEvaluationViewModel Evaluate(JsEvaluationViewModel model) + { + IJsEngine engine = null; + var result = new JsEvaluationResultViewModel(); + + try + { + engine = _engineSwitcher.CreateEngine(model.EngineName); + result.Value = engine.Evaluate(model.Expression); + } + catch (JsScriptException e) + { + var error = GetJsEvaluationErrorFromException(e); + error.LineNumber = e.LineNumber; + error.ColumnNumber = e.ColumnNumber; + error.SourceFragment = e.SourceFragment; + + result.Errors.Add(error); + } + catch (JsException e) + { + var error = GetJsEvaluationErrorFromException(e); + result.Errors.Add(error); + } + finally + { + if (engine != null) + { + engine.Dispose(); + } + } + + model.Result = result; + + return model; + } + + private static JsEvaluationErrorViewModel GetJsEvaluationErrorFromException(JsException jsException) + { + var jsError = new JsEvaluationErrorViewModel + { + EngineName = GetEngineDisplayName(jsException.EngineName), + EngineVersion = jsException.EngineVersion, + Message = jsException.Message + }; + + return jsError; + } + + private static string GetEngineDisplayName(string engineName) + { + string displayName = engineName; + const string postfix = "JsEngine"; + + if (_engineDisplayNameMappings.ContainsKey(engineName)) + { + displayName = _engineDisplayNameMappings[engineName]; + } + else + { + if (engineName.EndsWith(postfix)) + { + int displayNameLength = engineName.Length - postfix.Length; + if (displayNameLength > 0) + { + displayName = engineName.Substring(0, displayNameLength); + } + } + } + + return displayName; + } + } +} \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.Resources/CommonStrings.Designer.cs b/samples/JavaScriptEngineSwitcher.Sample.Resources/CommonStrings.Designer.cs new file mode 100644 index 00000000..3e6c3446 --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.Resources/CommonStrings.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +namespace JavaScriptEngineSwitcher.Sample.Resources +{ + using System; + using System.Globalization; + using System.Reflection; + using System.Resources; + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + public class CommonStrings + { + private static Lazy _resourceManager = + new Lazy(() => new ResourceManager( + "JavaScriptEngineSwitcher.Sample.Resources.CommonStrings", +#if NET20 || NET30 || NET35 || NET40 + typeof(CommonStrings).Assembly +#else + typeof(CommonStrings).GetTypeInfo().Assembly +#endif + )); + + private static CultureInfo _resourceCulture; + + /// + /// Returns a cached ResourceManager instance used by this class + /// + public static ResourceManager ResourceManager + { + get + { + return _resourceManager.Value; + } + } + + /// + /// Overrides a current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class + /// + public static CultureInfo Culture + { + get + { + return _resourceCulture; + } + set + { + _resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to "File '{0}' not exist." + /// + public static string ErrorMessage_FileNotFound + { + get { return GetString("ErrorMessage_FileNotFound"); } + } + + /// + /// Looks up a localized string similar to "File path not specified." + /// + public static string ErrorMessage_FilePathNotSpecified + { + get { return GetString("ErrorMessage_FilePathNotSpecified"); } + } + + /// + /// Looks up a localized string similar to "During reading of the file '{0}' an unknown error has occurred." + /// + public static string ErrorMessage_FileReadingFailed + { + get { return GetString("ErrorMessage_FileReadingFailed"); } + } + + private static string GetString(string name) + { + string value = ResourceManager.GetString(name, _resourceCulture); + + return value; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/Resources/Strings.resx b/samples/JavaScriptEngineSwitcher.Sample.Resources/CommonStrings.resx similarity index 93% rename from src/JavaScriptEngineSwitcher.Jint/Resources/Strings.resx rename to samples/JavaScriptEngineSwitcher.Sample.Resources/CommonStrings.resx index 3a141333..3d9148ac 100644 --- a/src/JavaScriptEngineSwitcher.Jint/Resources/Strings.resx +++ b/samples/JavaScriptEngineSwitcher.Sample.Resources/CommonStrings.resx @@ -117,13 +117,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Maximum execution time exceeded. + + File '{0}' not exist. - - Recursion is forbidden by script host: {0} + + File path not specified. - - Maximum number of statements executed have been reached. + + During reading of the file '{0}' an unknown error has occurred. \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.Resources/EvaluationStrings.Designer.cs b/samples/JavaScriptEngineSwitcher.Sample.Resources/EvaluationStrings.Designer.cs new file mode 100644 index 00000000..14fa940f --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.Resources/EvaluationStrings.Designer.cs @@ -0,0 +1,115 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +namespace JavaScriptEngineSwitcher.Sample.Resources +{ + using System; + using System.Globalization; + using System.Reflection; + using System.Resources; + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + public class EvaluationStrings + { + private static Lazy _resourceManager = + new Lazy(() => new ResourceManager( + "JavaScriptEngineSwitcher.Sample.Resources.EvaluationStrings", +#if NET20 || NET30 || NET35 || NET40 + typeof(EvaluationStrings).Assembly +#else + typeof(EvaluationStrings).GetTypeInfo().Assembly +#endif + )); + + private static CultureInfo _resourceCulture; + + /// + /// Returns a cached ResourceManager instance used by this class + /// + public static ResourceManager ResourceManager + { + get + { + return _resourceManager.Value; + } + } + + /// + /// Overrides a current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class + /// + public static CultureInfo Culture + { + get + { + return _resourceCulture; + } + set + { + _resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to "Evaluate" + /// + public static string ButtonText_Evaluate + { + get { return GetString("ButtonText_Evaluate"); } + } + + /// + /// Looks up a localized string similar to "Engine" + /// + public static string DisplayName_EngineName + { + get { return GetString("DisplayName_EngineName"); } + } + + /// + /// Looks up a localized string similar to "Expression" + /// + public static string DisplayName_Expression + { + get { return GetString("DisplayName_Expression"); } + } + + /// + /// Looks up a localized string similar to "Result" + /// + public static string DisplayName_ResultValue + { + get { return GetString("DisplayName_ResultValue"); } + } + + /// + /// Looks up a localized string similar to "You did not fill field "{0}"." + /// + public static string ErrorMessage_FormFieldIsNotFilled + { + get { return GetString("ErrorMessage_FormFieldIsNotFilled"); } + } + + /// + /// Looks up a localized string similar to "The field "{0}" must be a string with a maximum length of {1:N0}." + /// + public static string ErrorMessage_FormFieldValueTooLong + { + get { return GetString("ErrorMessage_FormFieldValueTooLong"); } + } + + private static string GetString(string name) + { + string value = ResourceManager.GetString(name, _resourceCulture); + + return value; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/Resources/Strings.ru-ru.resx b/samples/JavaScriptEngineSwitcher.Sample.Resources/EvaluationStrings.resx similarity index 89% rename from src/JavaScriptEngineSwitcher.V8/Resources/Strings.ru-ru.resx rename to samples/JavaScriptEngineSwitcher.Sample.Resources/EvaluationStrings.resx index 1a4c523d..1b24a4f2 100644 --- a/src/JavaScriptEngineSwitcher.V8/Resources/Strings.ru-ru.resx +++ b/samples/JavaScriptEngineSwitcher.Sample.Resources/EvaluationStrings.resx @@ -117,13 +117,22 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Не удалось загрузить значение `undefined` для ClearScript! + + Evaluate - - Не удалось загрузить сборку ClearScriptV8, потому что директория "{0}" не существует. + + Engine - - Не удалось загрузить сборку ClearScriptV8, потому что файл "{0}" не существует. + + Expression + + + Result + + + You did not fill field "{0}". + + + The field "{0}" must be a string with a maximum length of {1:N0}. \ No newline at end of file diff --git a/samples/JavaScriptEngineSwitcher.Sample.Resources/JavaScriptEngineSwitcher.Sample.Resources.csproj b/samples/JavaScriptEngineSwitcher.Sample.Resources/JavaScriptEngineSwitcher.Sample.Resources.csproj new file mode 100644 index 00000000..21b8c5cb --- /dev/null +++ b/samples/JavaScriptEngineSwitcher.Sample.Resources/JavaScriptEngineSwitcher.Sample.Resources.csproj @@ -0,0 +1,27 @@ + + + + JS Engine Switcher: Resources for Samples + 3.30.2 + net40-client;net45;net471;netstandard1.3;netstandard2.0 + 1.6.0 + Library + true + $(NoWarn);NETSDK1215;NU1903 + false + true + false + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/SharedData/text-content/contact.html b/samples/SharedData/text-content/contact.html new file mode 100644 index 00000000..7b72dd94 --- /dev/null +++ b/samples/SharedData/text-content/contact.html @@ -0,0 +1,13 @@ +

JavaScript Engine Switcher is an open source project, created and maintained by Andrey Taritsyn.

+ +

Project Site

+https://github.com/Taritsyn/JavaScriptEngineSwitcher + +

Email

+

taritsyn@gmail.com

+ +

Personal Site

+

www.taritsyn.ru

+ +

Mastodon

+

@taritsyn@fosstodon.org

\ No newline at end of file diff --git a/samples/SharedData/text-content/index.html b/samples/SharedData/text-content/index.html new file mode 100644 index 00000000..3f876a89 --- /dev/null +++ b/samples/SharedData/text-content/index.html @@ -0,0 +1,2 @@ +

JavaScript Engine Switcher determines unified interface for access to the basic features of popular JavaScript engines (ChakraCore, Jint, Jurassic, MSIE JavaScript Engine for .NET, NiL.JS, Jering.Javascript.NodeJS, Microsoft ClearScript.V8, VroomJs and YantraJS). This library allows you to quickly and easily switch to using of another JavaScript engine.

+

JavaScript Engine Switcher was created and is maintained by Andrey Taritsyn.

\ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64.csproj b/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64.csproj new file mode 100644 index 00000000..40c96426 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64.csproj @@ -0,0 +1,21 @@ + + + + JS Engine Switcher: ChakraCore for Linux (x64) + 3.27.3 + netstandard2.0 + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + This package complements the JavaScriptEngineSwitcher.ChakraCore package and contains the native implementation of ChakraCore for Linux (x64). + $(PackageCommonTags);ChakraCore;Linux;x64 + ChakraCore was updated to version of August 1, 2024. + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64.nuspec b/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64.nuspec new file mode 100644 index 00000000..d5a1567f --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64.nuspec @@ -0,0 +1,11 @@ + + + + $CommonMetadataElements$ + + + $CommonFileElements$ + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..202c0c2b --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/PACKAGE-DESCRIPTION.md @@ -0,0 +1,3 @@ +This package complements the [JavaScriptEngineSwitcher.ChakraCore](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore) package and contains the native implementation of [ChakraCore](https://github.com/chakra-core/ChakraCore) version of August 1, 2024 for Linux (x64). + +This package is only compatible with .NET Core. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/readme.txt b/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/readme.txt new file mode 100644 index 00000000..fd2f8f3c --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64/readme.txt @@ -0,0 +1,29 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: ChakraCore for Linux x64 v3.27.3 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + This package complements the JavaScriptEngineSwitcher.ChakraCore package and + contains the native implementation of ChakraCore version of August 1, 2024 + for Linux (x64). + + This package is only compatible with .NET Core. + + ============= + RELEASE NOTES + ============= + ChakraCore was updated to version of August 1, 2024. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64.csproj b/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64.csproj new file mode 100644 index 00000000..16d5be07 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64.csproj @@ -0,0 +1,21 @@ + + + + JS Engine Switcher: ChakraCore for OS X (x64) + 3.27.3 + netstandard2.0 + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + This package complements the JavaScriptEngineSwitcher.ChakraCore package and contains the native implementation of ChakraCore for OS X (x64). + $(PackageCommonTags);ChakraCore;macOS;OSX;x64 + ChakraCore was updated to version of August 1, 2024. + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64.nuspec b/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64.nuspec new file mode 100644 index 00000000..a9105540 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64.nuspec @@ -0,0 +1,11 @@ + + + + $CommonMetadataElements$ + + + $CommonFileElements$ + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..910cd8d8 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/PACKAGE-DESCRIPTION.md @@ -0,0 +1,3 @@ +This package complements the [JavaScriptEngineSwitcher.ChakraCore](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore) package and contains the native implementation of [ChakraCore](https://github.com/chakra-core/ChakraCore) version of August 1, 2024 for OS X (x64). + +This package is only compatible with .NET Core. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/readme.txt b/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/readme.txt new file mode 100644 index 00000000..2b84ec5b --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64/readme.txt @@ -0,0 +1,29 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: ChakraCore for OS X x64 v3.27.3 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + This package complements the JavaScriptEngineSwitcher.ChakraCore package and + contains the native implementation of ChakraCore version of August 1, 2024 + for OS X (x64). + + This package is only compatible with .NET Core. + + ============= + RELEASE NOTES + ============= + ChakraCore was updated to version of August 1, 2024. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm.csproj b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm.csproj new file mode 100644 index 00000000..4c281d94 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm.csproj @@ -0,0 +1,21 @@ + + + + JS Engine Switcher: ChakraCore for Windows (ARM) + 3.27.3 + netstandard2.0 + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + This package complements the JavaScriptEngineSwitcher.ChakraCore package and contains the native implementation of ChakraCore for Windows (ARM). + $(PackageCommonTags);ChakraCore;Windows;ARM + ChakraCore was updated to version of August 1, 2024. + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm.nuspec b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm.nuspec new file mode 100644 index 00000000..bd55ce3c --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm.nuspec @@ -0,0 +1,14 @@ + + + + $CommonMetadataElements$ + + + $CommonFileElements$ + + + + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..0e15d877 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/PACKAGE-DESCRIPTION.md @@ -0,0 +1,3 @@ +This package complements the [JavaScriptEngineSwitcher.ChakraCore](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore) package and contains the native implementation of [ChakraCore](https://github.com/chakra-core/ChakraCore) version of August 1, 2024 for Windows (ARM). + +This package is only compatible with .NET Core and .NET Framework 4.5. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm.props b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm.props new file mode 100644 index 00000000..bedb5851 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm.props @@ -0,0 +1,16 @@ + + + + + arm/%(Filename)%(Extension) + PreserveNewest + False + + + + %(Filename)%(Extension) + PreserveNewest + False + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/readme.txt b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/readme.txt new file mode 100644 index 00000000..5f8ec868 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/readme.txt @@ -0,0 +1,29 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: ChakraCore for Windows ARM v3.27.3 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + This package complements the JavaScriptEngineSwitcher.ChakraCore package and + contains the native implementation of ChakraCore version of August 1, 2024 + for Windows (ARM). + + This package is only compatible with .NET Core and .NET Framework 4.5. + + ============= + RELEASE NOTES + ============= + ChakraCore was updated to version of August 1, 2024. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/tools/Install.ps1 b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/tools/Install.ps1 new file mode 100644 index 00000000..9cbe0573 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/tools/Install.ps1 @@ -0,0 +1,13 @@ +param($installPath, $toolsPath, $package, $project) + +if ($project.Type -eq 'Web Site') { + $projectDir = $project.Properties.Item('FullPath').Value + + $assemblyDestDir = Join-Path $projectDir 'bin/arm' + if (!(Test-Path $assemblyDestDir)) { + New-Item -ItemType Directory -Force -Path $assemblyDestDir + } + + $assemblySourceFiles = Join-Path $installPath 'runtimes/win-arm/native/*.*' + Copy-Item $assemblySourceFiles $assemblyDestDir -Force +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/tools/Uninstall.ps1 b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/tools/Uninstall.ps1 new file mode 100644 index 00000000..fe5bab11 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm/tools/Uninstall.ps1 @@ -0,0 +1,13 @@ +param($installPath, $toolsPath, $package, $project) + +if ($project.Type -eq 'Web Site') { + $projectDir = $project.Properties.Item('FullPath').Value + $assemblySourceFiles = Join-Path $installPath 'runtimes/win-arm/native/*.*' + + foreach ($assemblySourceFileInfo in Get-Item($assemblySourceFiles)) { + $assemblyFile = Join-Path $projectDir "bin/arm/$($assemblySourceFileInfo.Name)" + if (Test-Path $assemblyFile) { + Remove-Item $assemblyFile -Force + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64.csproj b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64.csproj new file mode 100644 index 00000000..8e1d3262 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64.csproj @@ -0,0 +1,21 @@ + + + + JS Engine Switcher: ChakraCore for Windows (ARM64) + 3.27.3 + netstandard2.0 + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + This package complements the JavaScriptEngineSwitcher.ChakraCore package and contains the native implementation of ChakraCore for Windows (ARM64). + $(PackageCommonTags);ChakraCore;Windows;ARM64 + ChakraCore was updated to version of August 1, 2024. + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64.nuspec b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64.nuspec new file mode 100644 index 00000000..be7a9192 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64.nuspec @@ -0,0 +1,14 @@ + + + + $CommonMetadataElements$ + + + $CommonFileElements$ + + + + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..70ef2878 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/PACKAGE-DESCRIPTION.md @@ -0,0 +1,3 @@ +This package complements the [JavaScriptEngineSwitcher.ChakraCore](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore) package and contains the native implementation of [ChakraCore](https://github.com/chakra-core/ChakraCore) version of August 1, 2024 for Windows (ARM64). + +This package is only compatible with .NET Core and .NET Framework 4.5. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64.props b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64.props new file mode 100644 index 00000000..f783db92 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64.props @@ -0,0 +1,16 @@ + + + + + arm64/%(Filename)%(Extension) + PreserveNewest + False + + + + %(Filename)%(Extension) + PreserveNewest + False + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/readme.txt b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/readme.txt new file mode 100644 index 00000000..d6809f35 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/readme.txt @@ -0,0 +1,29 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: ChakraCore for Windows ARM64 v3.27.3 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + This package complements the JavaScriptEngineSwitcher.ChakraCore package and + contains the native implementation of ChakraCore version of August 1, 2024 + for Windows (ARM64). + + This package is only compatible with .NET Core and .NET Framework 4.5. + + ============= + RELEASE NOTES + ============= + ChakraCore was updated to version of August 1, 2024. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/tools/Install.ps1 b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/tools/Install.ps1 new file mode 100644 index 00000000..b16c3f75 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/tools/Install.ps1 @@ -0,0 +1,13 @@ +param($installPath, $toolsPath, $package, $project) + +if ($project.Type -eq 'Web Site') { + $projectDir = $project.Properties.Item('FullPath').Value + + $assemblyDestDir = Join-Path $projectDir 'bin/arm64' + if (!(Test-Path $assemblyDestDir)) { + New-Item -ItemType Directory -Force -Path $assemblyDestDir + } + + $assemblySourceFiles = Join-Path $installPath 'runtimes/win-arm64/native/*.*' + Copy-Item $assemblySourceFiles $assemblyDestDir -Force +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/tools/Uninstall.ps1 b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/tools/Uninstall.ps1 new file mode 100644 index 00000000..f1fa4b6c --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64/tools/Uninstall.ps1 @@ -0,0 +1,13 @@ +param($installPath, $toolsPath, $package, $project) + +if ($project.Type -eq 'Web Site') { + $projectDir = $project.Properties.Item('FullPath').Value + $assemblySourceFiles = Join-Path $installPath 'runtimes/win-arm64/native/*.*' + + foreach ($assemblySourceFileInfo in Get-Item($assemblySourceFiles)) { + $assemblyFile = Join-Path $projectDir "bin/arm64/$($assemblySourceFileInfo.Name)" + if (Test-Path $assemblyFile) { + Remove-Item $assemblyFile -Force + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64.csproj b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64.csproj new file mode 100644 index 00000000..26562fe9 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64.csproj @@ -0,0 +1,21 @@ + + + + JS Engine Switcher: ChakraCore for Windows (x64) + 3.27.3 + netstandard2.0 + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + This package complements the JavaScriptEngineSwitcher.ChakraCore package and contains the native implementation of ChakraCore for Windows (x64). + $(PackageCommonTags);ChakraCore;Windows;x64 + ChakraCore was updated to version of August 1, 2024. + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64.nuspec b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64.nuspec new file mode 100644 index 00000000..dd6618a8 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64.nuspec @@ -0,0 +1,14 @@ + + + + $CommonMetadataElements$ + + + $CommonFileElements$ + + + + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..0cd8c83c --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/PACKAGE-DESCRIPTION.md @@ -0,0 +1 @@ +This package complements the [JavaScriptEngineSwitcher.ChakraCore](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore) package and contains the native implementation of [ChakraCore](https://github.com/chakra-core/ChakraCore) version of August 1, 2024 for Windows (x64). \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64.props b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64.props new file mode 100644 index 00000000..5a187768 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64.props @@ -0,0 +1,16 @@ + + + + + x64/%(Filename)%(Extension) + PreserveNewest + False + + + + %(Filename)%(Extension) + PreserveNewest + False + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/readme.txt b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/readme.txt new file mode 100644 index 00000000..6ac75fb7 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/readme.txt @@ -0,0 +1,27 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: ChakraCore for Windows x64 v3.27.3 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + This package complements the JavaScriptEngineSwitcher.ChakraCore package and + contains the native implementation of ChakraCore version of August 1, 2024 + for Windows (x64). + + ============= + RELEASE NOTES + ============= + ChakraCore was updated to version of August 1, 2024. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/tools/Install.ps1 b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/tools/Install.ps1 new file mode 100644 index 00000000..f44b2b4e --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/tools/Install.ps1 @@ -0,0 +1,13 @@ +param($installPath, $toolsPath, $package, $project) + +if ($project.Type -eq 'Web Site') { + $projectDir = $project.Properties.Item('FullPath').Value + + $assemblyDestDir = Join-Path $projectDir 'bin/x64' + if (!(Test-Path $assemblyDestDir)) { + New-Item -ItemType Directory -Force -Path $assemblyDestDir + } + + $assemblySourceFiles = Join-Path $installPath 'runtimes/win-x64/native/*.*' + Copy-Item $assemblySourceFiles $assemblyDestDir -Force +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/tools/Uninstall.ps1 b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/tools/Uninstall.ps1 new file mode 100644 index 00000000..6443ab60 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64/tools/Uninstall.ps1 @@ -0,0 +1,13 @@ +param($installPath, $toolsPath, $package, $project) + +if ($project.Type -eq 'Web Site') { + $projectDir = $project.Properties.Item('FullPath').Value + $assemblySourceFiles = Join-Path $installPath 'runtimes/win-x64/native/*.*' + + foreach ($assemblySourceFileInfo in Get-Item($assemblySourceFiles)) { + $assemblyFile = Join-Path $projectDir "bin/x64/$($assemblySourceFileInfo.Name)" + if (Test-Path $assemblyFile) { + Remove-Item $assemblyFile -Force + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.csproj b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.csproj new file mode 100644 index 00000000..b90cd8f5 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.csproj @@ -0,0 +1,21 @@ + + + + JS Engine Switcher: ChakraCore for Windows (x86) + 3.27.3 + netstandard2.0 + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + This package complements the JavaScriptEngineSwitcher.ChakraCore package and contains the native implementation of ChakraCore for Windows (x86). + $(PackageCommonTags);ChakraCore;Windows;x86 + ChakraCore was updated to version of August 1, 2024. + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.nuspec b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.nuspec new file mode 100644 index 00000000..8b0bc5e3 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.nuspec @@ -0,0 +1,14 @@ + + + + $CommonMetadataElements$ + + + $CommonFileElements$ + + + + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..b6951ff8 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/PACKAGE-DESCRIPTION.md @@ -0,0 +1 @@ +This package complements the [JavaScriptEngineSwitcher.ChakraCore](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore) package and contains the native implementation of [ChakraCore](https://github.com/chakra-core/ChakraCore) version of August 1, 2024 for Windows (x86). \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.props b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.props new file mode 100644 index 00000000..be3fd380 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/build/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86.props @@ -0,0 +1,16 @@ + + + + + x86/%(Filename)%(Extension) + PreserveNewest + False + + + + %(Filename)%(Extension) + PreserveNewest + False + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/readme.txt b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/readme.txt new file mode 100644 index 00000000..ff0c8d12 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/readme.txt @@ -0,0 +1,27 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: ChakraCore for Windows x86 v3.27.3 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + This package complements the JavaScriptEngineSwitcher.ChakraCore package and + contains the native implementation of ChakraCore version of August 1, 2024 + for Windows (x86). + + ============= + RELEASE NOTES + ============= + ChakraCore was updated to version of August 1, 2024. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/tools/Install.ps1 b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/tools/Install.ps1 new file mode 100644 index 00000000..4a9e29e9 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/tools/Install.ps1 @@ -0,0 +1,13 @@ +param($installPath, $toolsPath, $package, $project) + +if ($project.Type -eq 'Web Site') { + $projectDir = $project.Properties.Item('FullPath').Value + + $assemblyDestDir = Join-Path $projectDir 'bin/x86' + if (!(Test-Path $assemblyDestDir)) { + New-Item -ItemType Directory -Force -Path $assemblyDestDir + } + + $assemblySourceFiles = Join-Path $installPath 'runtimes/win-x86/native/*.*' + Copy-Item $assemblySourceFiles $assemblyDestDir -Force +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/tools/Uninstall.ps1 b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/tools/Uninstall.ps1 new file mode 100644 index 00000000..6402ab1d --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86/tools/Uninstall.ps1 @@ -0,0 +1,13 @@ +param($installPath, $toolsPath, $package, $project) + +if ($project.Type -eq 'Web Site') { + $projectDir = $project.Properties.Item('FullPath').Value + $assemblySourceFiles = Join-Path $installPath 'runtimes/win-x86/native/*.*' + + foreach ($assemblySourceFileInfo in Get-Item($assemblySourceFiles)) { + $assemblyFile = Join-Path $projectDir "bin/x86/$($assemblySourceFileInfo.Name)" + if (Test-Path $assemblyFile) { + Remove-Item $assemblyFile -Force + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/AssemblyResolver.cs b/src/JavaScriptEngineSwitcher.ChakraCore/AssemblyResolver.cs index 2ba9a4e8..721caa7a 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/AssemblyResolver.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/AssemblyResolver.cs @@ -1,67 +1,66 @@ -namespace JavaScriptEngineSwitcher.ChakraCore -{ - using System; - using System.IO; - using System.Runtime.InteropServices; - using System.Text.RegularExpressions; +#if NETFRAMEWORK +using System; +using System.IO; +using System.Runtime.InteropServices; +#if NET40 + +using PolyfillsForOldDotNet.System.Runtime.InteropServices; +#endif + +using JavaScriptEngineSwitcher.Core.Utilities; - using Resources; +using JavaScriptEngineSwitcher.ChakraCore.Constants; +using JavaScriptEngineSwitcher.ChakraCore.Resources; +namespace JavaScriptEngineSwitcher.ChakraCore +{ /// /// Assembly resolver /// internal static class AssemblyResolver { - /// - /// Name of directory, that contains the ChakraCore assemblies - /// - private const string ASSEMBLY_DIRECTORY_NAME = "ChakraCore"; - - /// - /// Regular expression for working with the `bin` directory path - /// - private static readonly Regex _binDirectoryRegex = new Regex(@"\\bin\\?$", RegexOptions.IgnoreCase); - - - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [DllImport("kernel32.dll", SetLastError = true)] private static extern bool SetDllDirectory(string lpPathName); + /// /// Initialize a assembly resolver /// public static void Initialize() { - var currentDomain = AppDomain.CurrentDomain; - string platform = Environment.Is64BitProcess ? "x64" : "x86"; - - string binDirectoryPath = currentDomain.SetupInformation.PrivateBinPath; - if (string.IsNullOrEmpty(binDirectoryPath)) + AppDomain currentDomain = AppDomain.CurrentDomain; + string baseDirectoryPath = currentDomain.SetupInformation.PrivateBinPath; + if (string.IsNullOrEmpty(baseDirectoryPath)) { // `PrivateBinPath` property is empty in test scenarios, so // need to use the `BaseDirectory` property - binDirectoryPath = currentDomain.BaseDirectory; + baseDirectoryPath = currentDomain.BaseDirectory; } - string assemblyDirectoryPath = Path.Combine(binDirectoryPath, ASSEMBLY_DIRECTORY_NAME, platform); + Architecture architecture = RuntimeInformation.OSArchitecture; + string platform; - if (!Directory.Exists(assemblyDirectoryPath)) + if (architecture == Architecture.X64 || architecture == Architecture.X86) + { + platform = Utils.Is64BitProcess() ? "x64" : "x86"; + } + else if (architecture == Architecture.Arm64 || architecture == Architecture.Arm) { - if (_binDirectoryRegex.IsMatch(binDirectoryPath)) - { - string applicationRootPath = _binDirectoryRegex.Replace(binDirectoryPath, string.Empty); - assemblyDirectoryPath = Path.Combine(applicationRootPath, ASSEMBLY_DIRECTORY_NAME, platform); + platform = Utils.Is64BitProcess() ? "arm64" : "arm"; + } + else + { + return; + } - if (!Directory.Exists(assemblyDirectoryPath)) - { - throw new DirectoryNotFoundException( - string.Format(Strings.Engines_ChakraCoreAssemblyDirectoryNotFound, assemblyDirectoryPath)); - } - } - else - { - throw new DirectoryNotFoundException( - string.Format(Strings.Engines_ChakraCoreAssemblyDirectoryNotFound, assemblyDirectoryPath)); - } + string assemblyFileName = DllName.ForWindows; + string assemblyDirectoryPath = Path.Combine(baseDirectoryPath, platform); + string assemblyFilePath = Path.Combine(assemblyDirectoryPath, assemblyFileName); + bool assemblyFileExists = File.Exists(assemblyFilePath); + + if (!assemblyFileExists) + { + return; } if (!SetDllDirectory(assemblyDirectoryPath)) @@ -71,4 +70,5 @@ public static void Initialize() } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreJsEngine.cs b/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreJsEngine.cs index f927e68f..9a545b96 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreJsEngine.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreJsEngine.cs @@ -1,912 +1,881 @@ -namespace JavaScriptEngineSwitcher.ChakraCore +using System; +#if NET45_OR_GREATER || NETSTANDARD +using System.Runtime.InteropServices; +#endif +using System.Text; + +using AdvancedStringBuilder; +#if NET40 +using PolyfillsForOldDotNet.System.Runtime.InteropServices; +#endif + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Constants; +using JavaScriptEngineSwitcher.Core.Extensions; +using JavaScriptEngineSwitcher.Core.Utilities; + +using ErrorLocationItem = JavaScriptEngineSwitcher.Core.Helpers.ErrorLocationItem; +using CoreErrorHelpers = JavaScriptEngineSwitcher.Core.Helpers.JsErrorHelpers; +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; +using TextHelpers = JavaScriptEngineSwitcher.Core.Helpers.TextHelpers; +using WrapperCompilationException = JavaScriptEngineSwitcher.Core.JsCompilationException; +using WrapperEngineException = JavaScriptEngineSwitcher.Core.JsEngineException; +using WrapperEngineLoadException = JavaScriptEngineSwitcher.Core.JsEngineLoadException; +using WrapperException = JavaScriptEngineSwitcher.Core.JsException; +using WrapperFatalException = JavaScriptEngineSwitcher.Core.JsFatalException; +using WrapperInterruptedException = JavaScriptEngineSwitcher.Core.JsInterruptedException; +using WrapperRuntimeException = JavaScriptEngineSwitcher.Core.JsRuntimeException; +using WrapperScriptException = JavaScriptEngineSwitcher.Core.JsScriptException; +using WrapperUsageException = JavaScriptEngineSwitcher.Core.JsUsageException; + +using JavaScriptEngineSwitcher.ChakraCore.Constants; +using JavaScriptEngineSwitcher.ChakraCore.JsRt; +using JavaScriptEngineSwitcher.ChakraCore.Resources; + +using OriginalEngineException = JavaScriptEngineSwitcher.ChakraCore.JsRt.JsEngineException; +using OriginalException = JavaScriptEngineSwitcher.ChakraCore.JsRt.JsException; +using OriginalFatalException = JavaScriptEngineSwitcher.ChakraCore.JsRt.JsFatalException; +using OriginalScriptException = JavaScriptEngineSwitcher.ChakraCore.JsRt.JsScriptException; +using OriginalUsageException = JavaScriptEngineSwitcher.ChakraCore.JsRt.JsUsageException; + +namespace JavaScriptEngineSwitcher.ChakraCore { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using System.Reflection; - using System.Runtime.InteropServices; - - using OriginalJsException = JsRt.JsException; - - using Core; - using Core.Utilities; - using CoreStrings = Core.Resources.Strings; - - using Helpers; - using JsRt; - using Resources; - /// - /// Adapter for ChakraCore JavaScript engine + /// Adapter for the ChakraCore JS engine /// public sealed class ChakraCoreJsEngine : JsEngineBase { /// - /// Name of JavaScript engine + /// Name of JS engine /// - private const string ENGINE_NAME = "ChakraCore JavaScript engine"; + public const string EngineName = "ChakraCoreJsEngine"; /// - /// Version of original JavaScript engine + /// Version of original JS engine /// - private const string ENGINE_VERSION = "May 24, 2016"; + private const string EngineVersion = "Aug 1, 2024"; /// - /// Instance of JavaScript runtime + /// Instance of JS runtime /// private JsRuntime _jsRuntime; /// - /// Instance of JavaScript context + /// Instance of JS context /// - private readonly JsContext _jsContext; + private JsContext _jsContext; /// - /// Synchronizer of code execution + /// JS source context /// - private readonly object _executionSynchronizer = new object(); + private JsSourceContext _jsSourceContext = JsSourceContext.FromIntPtr(IntPtr.Zero); /// - /// List of external objects + /// Type mapper /// - private readonly ISet _externalObjects; + private TypeMapper _typeMapper; /// - /// Callback for finalization of external object + /// Callback for continuation of promise /// - private JsObjectFinalizeCallback _externalObjectFinalizeCallback; + private JsPromiseContinuationCallback _promiseContinuationCallback; /// - /// List of native function callbacks + /// Script dispatcher /// - private readonly ISet _nativeFunctions; + private ScriptDispatcher _dispatcher; /// - /// Gets a name of JavaScript engine + /// Unique document name manager /// - public override string Name - { - get { return ENGINE_NAME; } - } + private UniqueDocumentNameManager _documentNameManager = new UniqueDocumentNameManager(DefaultDocumentName); +#if NETFRAMEWORK /// - /// Gets a version of original JavaScript engine + /// Synchronizer of JS engine initialization /// - public override string Version - { - get { return ENGINE_VERSION; } - } - + private static readonly object _initializationSynchronizer = new object(); /// - /// Static constructor + /// Flag indicating whether the JS engine is initialized /// - static ChakraCoreJsEngine() - { - AssemblyResolver.Initialize(); - } + private static bool _initialized; +#endif + /// - /// Constructs a instance of adapter for ChakraCore JavaScript engine + /// Constructs an instance of adapter for the ChakraCore JS engine /// public ChakraCoreJsEngine() + : this(new ChakraCoreSettings()) + { } + + /// + /// Constructs an instance of adapter for the ChakraCore JS engine + /// + /// Settings of the ChakraCore JS engine + public ChakraCoreJsEngine(ChakraCoreSettings settings) { +#if NETFRAMEWORK + Initialize(); + +#endif + ChakraCoreSettings chakraCoreSettings = settings ?? new ChakraCoreSettings(); + + JsRuntimeAttributes attributes = JsRuntimeAttributes.AllowScriptInterrupt; + if (chakraCoreSettings.DisableBackgroundWork) + { + attributes |= JsRuntimeAttributes.DisableBackgroundWork; + } + if (chakraCoreSettings.DisableEval) + { + attributes |= JsRuntimeAttributes.DisableEval; + } + if (chakraCoreSettings.DisableExecutablePageAllocation) + { + attributes |= JsRuntimeAttributes.DisableExecutablePageAllocation; + } + if (chakraCoreSettings.DisableFatalOnOOM) + { + attributes |= JsRuntimeAttributes.DisableFatalOnOOM; + } + if (chakraCoreSettings.DisableNativeCodeGeneration) + { + attributes |= JsRuntimeAttributes.DisableNativeCodeGeneration; + } + if (chakraCoreSettings.EnableExperimentalFeatures) + { + attributes |= JsRuntimeAttributes.EnableExperimentalFeatures; + } + + _typeMapper = new TypeMapper(chakraCoreSettings.AllowReflection); +#if NETSTANDARD1_3 + _dispatcher = new ScriptDispatcher(); +#else + _dispatcher = new ScriptDispatcher(chakraCoreSettings.MaxStackSize); +#endif + _promiseContinuationCallback = PromiseContinuationCallback; + try { - _jsRuntime = JsRuntime.Create(JsRuntimeAttributes.AllowScriptInterrupt, JsRuntimeVersion.VersionEdge, null); - _jsContext = _jsRuntime.CreateContext(); + _dispatcher.Invoke(() => + { + _jsRuntime = JsRuntime.Create(attributes, null); + _jsRuntime.MemoryLimit = settings.MemoryLimit; + + _jsContext = _jsRuntime.CreateContext(); + if (_jsContext.IsValid) + { + _jsContext.AddRef(); + + using (new JsScope(_jsContext)) + { + JsContext.SetPromiseContinuationCallback(_promiseContinuationCallback, IntPtr.Zero); + } + } + }); + } + catch (DllNotFoundException e) + { + throw WrapDllNotFoundException(e); } catch (Exception e) { - throw new JsEngineLoadException( - string.Format(CoreStrings.Runtime_JsEngineNotLoaded, - ENGINE_NAME, e.Message), ENGINE_NAME, ENGINE_VERSION, e); + throw CoreErrorHelpers.WrapEngineLoadException(e, EngineName, EngineVersion, true); + } + finally + { + if (!_jsContext.IsValid) + { + Dispose(); + } } - - _externalObjects = new HashSet(); - _externalObjectFinalizeCallback = ExternalObjectFinalizeCallback; - _nativeFunctions = new HashSet(); } /// - /// Destructs instance of adapter for ChakraCore JavaScript engine + /// Destructs an instance of adapter for the ChakraCore JS engine /// ~ChakraCoreJsEngine() { Dispose(false); } +#if NETFRAMEWORK - private void InvokeScript(Action action) + /// + /// Initializes a JS engine + /// + private static void Initialize() { - lock (_executionSynchronizer) - using (new JsScope(_jsContext)) + if (_initialized) { - try - { - action(); - } - catch (OriginalJsException e) - { - throw ConvertJsExceptionToJsRuntimeException(e); - } + return; } - } - private T InvokeScript(Func func) - { - lock (_executionSynchronizer) - using (new JsScope(_jsContext)) + lock (_initializationSynchronizer) { - try + if (_initialized) { - return func(); + return; } - catch (OriginalJsException e) + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - throw ConvertJsExceptionToJsRuntimeException(e); + try + { + AssemblyResolver.Initialize(); + } + catch (InvalidOperationException e) + { + throw CoreErrorHelpers.WrapEngineLoadException(e, EngineName, EngineVersion); + } } + + _initialized = true; } } +#endif /// - /// Destroys object + /// Adds a reference to the value /// - /// Flag, allowing destruction of - /// managed objects contained in fields of class - private void Dispose(bool disposing) + /// The value + private static void AddReferenceToValue(JsValue value) { - if (_disposedFlag.Set()) + if (CanHaveReferences(value)) { - lock (_executionSynchronizer) - { - _jsRuntime.Dispose(); - - _externalObjects.Clear(); - _nativeFunctions.Clear(); - _externalObjectFinalizeCallback = null; - } + value.AddRef(); } } - #region Mapping - /// - /// Makes a mapping of value from the host type to a script type + /// Removes a reference to the value /// - /// The source value - /// The mapped value - private JsValue MapToScriptType(object value) + /// The value + private static void RemoveReferenceToValue(JsValue value) { - if (value == null) + if (CanHaveReferences(value)) { - return JsValue.Null; - } - - if (value is Undefined) - { - return JsValue.Undefined; - } - - TypeCode typeCode = Type.GetTypeCode(value.GetType()); - - switch (typeCode) - { - case TypeCode.Boolean: - return JsValue.FromBoolean((bool)value); - - case TypeCode.SByte: - case TypeCode.Byte: - case TypeCode.Int16: - case TypeCode.UInt16: - case TypeCode.Int32: - case TypeCode.UInt32: - case TypeCode.Int64: - case TypeCode.UInt64: - return JsValue.FromInt32(Convert.ToInt32(value)); - - case TypeCode.Single: - case TypeCode.Double: - case TypeCode.Decimal: - return JsValue.FromDouble(Convert.ToDouble(value)); - - case TypeCode.Char: - case TypeCode.String: - return JsValue.FromString((string)value); - - default: - return FromObject(value); + value.Release(); } } /// - /// Makes a mapping of array items from the host type to a script type - /// - /// The source array - /// The mapped array - private JsValue[] MapToScriptType(object[] args) - { - return args.Select(MapToScriptType).ToArray(); - } - - /// - /// Makes a mapping of value from the script type to a host type + /// Checks whether the value can have references /// - /// The source value - /// The mapped value - private object MapToHostType(JsValue value) + /// The value + /// Result of check (true - may have; false - may not have) + private static bool CanHaveReferences(JsValue value) { JsValueType valueType = value.ValueType; - JsValue processedValue; - object result; switch (valueType) { case JsValueType.Null: - result = null; - break; case JsValueType.Undefined: - result = Undefined.Value; - break; case JsValueType.Boolean: - processedValue = value.ConvertToBoolean(); - result = processedValue.ToBoolean(); - break; - case JsValueType.Number: - processedValue = value.ConvertToNumber(); - result = NumericHelpers.CastDoubleValueToCorrectType(processedValue.ToDouble()); - break; - case JsValueType.String: - processedValue = value.ConvertToString(); - result = processedValue.ToString(); - break; - case JsValueType.Object: - case JsValueType.Function: - case JsValueType.Error: - case JsValueType.Array: - case JsValueType.Symbol: - case JsValueType.ArrayBuffer: - case JsValueType.TypedArray: - case JsValueType.DataView: - result = ToObject(value); - break; + return false; default: - throw new ArgumentOutOfRangeException(); + return true; } - - return result; } /// - /// Makes a mapping of array items from the script type to a host type + /// The promise continuation callback /// - /// The source array - /// The mapped array - private object[] MapToHostType(JsValue[] args) - { - return args.Select(MapToHostType).ToArray(); - } - - private JsValue FromObject(object value) - { - var del = value as Delegate; - JsValue objValue = del != null ? CreateFunctionFromDelegate(del) : CreateExternalObjectFromObject(value); - - return objValue; - } - - private object ToObject(JsValue value) - { - object result = value.HasExternalData ? - GCHandle.FromIntPtr(value.ExternalData).Target : value.ConvertToObject(); - - return result; - } - - private JsValue CreateExternalObjectFromObject(object value) - { - GCHandle handle = GCHandle.Alloc(value); - _externalObjects.Add(value); - - JsValue objValue = JsValue.CreateExternalObject( - GCHandle.ToIntPtr(handle), _externalObjectFinalizeCallback); - Type type = value.GetType(); - - ProjectFields(objValue, type, true); - ProjectProperties(objValue, type, true); - ProjectMethods(objValue, type, true); - FreezeObject(objValue); - - return objValue; - } - - private void ExternalObjectFinalizeCallback(IntPtr data) + /// The task, represented as a JavaScript function + /// The data argument to be passed to the callback + private static void PromiseContinuationCallback(JsValue task, IntPtr callbackState) { - if (data == IntPtr.Zero) - { - return; - } + task.AddRef(); - GCHandle handle = GCHandle.FromIntPtr(data); - object obj = handle.Target; - - if (obj == null) + try { - return; + task.CallFunction(JsValue.GlobalObject); } - - lock (_executionSynchronizer) + finally { - _externalObjects.Remove(obj); + task.Release(); } } - private JsValue CreateObjectFromType(Type type) - { - JsValue typeValue = CreateConstructor(type); - - ProjectFields(typeValue, type, false); - ProjectProperties(typeValue, type, false); - ProjectMethods(typeValue, type, false); - FreezeObject(typeValue); - - return typeValue; - } + #region Mapping - private void FreezeObject(JsValue objValue) + private WrapperException WrapJsException(OriginalException originalException, + string defaultDocumentName = null) { - JsValue freezeMethodValue = JsValue.GlobalObject - .GetProperty("Object") - .GetProperty("freeze") - ; - freezeMethodValue.CallFunction(objValue); - } + WrapperException wrapperException; + JsErrorCode errorCode = originalException.ErrorCode; + string description = originalException.Message; + string message = description; + string type = string.Empty; + string documentName = defaultDocumentName ?? string.Empty; + int lineNumber = 0; + int columnNumber = 0; + string callStack = string.Empty; + string sourceFragment = string.Empty; - private JsValue CreateFunctionFromDelegate(Delegate value) - { - JsNativeFunction nativeFunction = (callee, isConstructCall, args, argCount, callbackData) => + var originalScriptException = originalException as OriginalScriptException; + if (originalScriptException != null) { - object[] processedArgs = MapToHostType(args.Skip(1).ToArray()); - JsValue undefinedValue = JsValue.Undefined; + JsValue metadataValue = originalScriptException.Metadata; + + if (metadataValue.IsValid) + { + JsValue errorValue = metadataValue.GetProperty("exception"); + JsValueType errorValueType = errorValue.ValueType; - ReflectionHelpers.FixArgumentTypes(ref processedArgs, value.Method.GetParameters()); + if (errorValueType == JsValueType.Error + || errorValueType == JsValueType.Object) + { + JsPropertyId innerErrorPropertyId = JsPropertyId.FromString("innerException"); + if (errorValue.HasProperty(innerErrorPropertyId)) + { + JsValue innerErrorValue = errorValue.GetProperty(innerErrorPropertyId); + JsPropertyId metadataPropertyId = JsPropertyId.FromString("metadata"); - object result; + if (innerErrorValue.HasProperty(metadataPropertyId)) + { + errorValue = innerErrorValue; + metadataValue = innerErrorValue.GetProperty(metadataPropertyId); + } + } - try - { - result = value.DynamicInvoke(processedArgs); - } - catch (Exception e) - { - JsValue errorValue = JsErrorHelpers.CreateError( - string.Format(Strings.Runtime_HostDelegateInvocationFailed, e.Message)); - JsErrorHelpers.SetException(errorValue); + JsValue messagePropertyValue = errorValue.GetProperty("message"); + string localDescription = messagePropertyValue.ConvertToString().ToString(); + if (!string.IsNullOrWhiteSpace(localDescription)) + { + description = localDescription; + } - return undefinedValue; - } + JsValue namePropertyValue = errorValue.GetProperty("name"); + type = namePropertyValue.ValueType == JsValueType.String ? + namePropertyValue.ToString() : string.Empty; - JsValue resultValue = MapToScriptType(result); + JsPropertyId descriptionPropertyId = JsPropertyId.FromString("description"); + if (errorValue.HasProperty(descriptionPropertyId)) + { + JsValue descriptionPropertyValue = errorValue.GetProperty(descriptionPropertyId); + localDescription = descriptionPropertyValue.ConvertToString().ToString(); + if (!string.IsNullOrWhiteSpace(localDescription)) + { + description = localDescription; + } + } - return resultValue; - }; - _nativeFunctions.Add(nativeFunction); + if (type == JsErrorType.Syntax) + { + errorCode = JsErrorCode.ScriptCompile; + } + else + { + JsPropertyId numberPropertyId = JsPropertyId.FromString("number"); + if (errorValue.HasProperty(numberPropertyId)) + { + JsValue numberPropertyValue = errorValue.GetProperty(numberPropertyId); + int errorNumber = numberPropertyValue.ValueType == JsValueType.Number ? + numberPropertyValue.ToInt32() : 0; + errorCode = (JsErrorCode)errorNumber; + } + } - JsValue functionValue = JsValue.CreateFunction(nativeFunction); + JsPropertyId urlPropertyId = JsPropertyId.FromString("url"); + if (metadataValue.HasProperty(urlPropertyId)) + { + JsValue urlPropertyValue = metadataValue.GetProperty(urlPropertyId); + string url = urlPropertyValue.ValueType == JsValueType.String ? + urlPropertyValue.ToString() : string.Empty; + if (url != "undefined") + { + documentName = url; + } + } - return functionValue; - } + JsPropertyId linePropertyId = JsPropertyId.FromString("line"); + if (metadataValue.HasProperty(linePropertyId)) + { + JsValue linePropertyValue = metadataValue.GetProperty(linePropertyId); + lineNumber = linePropertyValue.ValueType == JsValueType.Number ? + linePropertyValue.ToInt32() + 1 : 0; + } - private JsValue CreateConstructor(Type type) - { - string typeName = type.FullName; - BindingFlags defaultBindingFlags = ReflectionHelpers.GetDefaultBindingFlags(true); - ConstructorInfo[] constructors = type.GetConstructors(defaultBindingFlags); + JsPropertyId columnPropertyId = JsPropertyId.FromString("column"); + if (metadataValue.HasProperty(columnPropertyId)) + { + JsValue columnPropertyValue = metadataValue.GetProperty(columnPropertyId); + columnNumber = columnPropertyValue.ValueType == JsValueType.Number ? + columnPropertyValue.ToInt32() + 1 : 0; + } - JsNativeFunction nativeFunction = (callee, isConstructCall, args, argCount, callbackData) => - { - JsValue resultValue; - JsValue undefinedValue = JsValue.Undefined; + string sourceLine = string.Empty; + JsPropertyId sourcePropertyId = JsPropertyId.FromString("source"); + if (metadataValue.HasProperty(sourcePropertyId)) + { + JsValue sourcePropertyValue = metadataValue.GetProperty(sourcePropertyId); + sourceLine = sourcePropertyValue.ValueType == JsValueType.String ? + sourcePropertyValue.ToString() : string.Empty; + if (sourceLine != "undefined") + { + sourceFragment = TextHelpers.GetTextFragmentFromLine(sourceLine, columnNumber); + } + } - object[] processedArgs = MapToHostType(args.Skip(1).ToArray()); - object result; + JsPropertyId stackPropertyId = JsPropertyId.FromString("stack"); + if (errorValue.HasProperty(stackPropertyId)) + { + JsValue stackPropertyValue = errorValue.GetProperty(stackPropertyId); + string messageWithTypeAndCallStack = stackPropertyValue.ValueType == JsValueType.String ? + stackPropertyValue.ToString() : string.Empty; + string messageWithType = errorValue.ConvertToString().ToString(); + string rawCallStack = messageWithTypeAndCallStack + .TrimStart(messageWithType) + .TrimStart("Error") + .TrimStart(new char[] { '\n', '\r' }) + ; + string callStackWithSourceFragment = string.Empty; - if (processedArgs.Length == 0 && type.IsValueType) - { - result = Activator.CreateInstance(type); - resultValue = MapToScriptType(result); + ErrorLocationItem[] callStackItems = CoreErrorHelpers.ParseErrorLocation(rawCallStack); + if (callStackItems.Length > 0) + { + ErrorLocationItem firstCallStackItem = callStackItems[0]; + firstCallStackItem.SourceFragment = sourceFragment; + + documentName = firstCallStackItem.DocumentName; + lineNumber = firstCallStackItem.LineNumber; + columnNumber = firstCallStackItem.ColumnNumber; + callStack = CoreErrorHelpers.StringifyErrorLocationItems(callStackItems, true); + callStackWithSourceFragment = CoreErrorHelpers.StringifyErrorLocationItems(callStackItems); + } - return resultValue; + message = CoreErrorHelpers.GenerateScriptErrorMessage(type, description, + callStackWithSourceFragment); + } + else + { + message = CoreErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, + lineNumber, columnNumber, sourceFragment); + } + } + else if (errorValueType == JsValueType.String) + { + message = errorValue.ToString(); + description = message; + } + else + { + message = errorValue.ConvertToString().ToString(); + description = message; + } } - if (constructors.Length == 0) + WrapperScriptException wrapperScriptException; + if (errorCode == JsErrorCode.ScriptCompile) { - JsValue errorValue = JsErrorHelpers.CreateError( - string.Format(Strings.Runtime_HostTypeConstructorNotFound, typeName)); - JsErrorHelpers.SetException(errorValue); - - return undefinedValue; + wrapperScriptException = new WrapperCompilationException(message, EngineName, EngineVersion, + originalScriptException); } - - var bestFitConstructor = (ConstructorInfo)ReflectionHelpers.GetBestFitMethod( - constructors, processedArgs); - if (bestFitConstructor == null) + else if (errorCode == JsErrorCode.ScriptTerminated) { - JsValue errorValue = JsErrorHelpers.CreateReferenceError( - string.Format(Strings.Runtime_SuitableConstructorOfHostTypeNotFound, typeName)); - JsErrorHelpers.SetException(errorValue); + message = CoreStrings.Runtime_ScriptInterrupted; + description = message; - return undefinedValue; - } + wrapperScriptException = new WrapperInterruptedException(message, + EngineName, EngineVersion, originalScriptException) + { + CallStack = callStack + }; - ReflectionHelpers.FixArgumentTypes(ref processedArgs, bestFitConstructor.GetParameters()); + // Restore a JS engine after interruption + _jsRuntime.Disabled = false; + } + else + { + wrapperScriptException = new WrapperRuntimeException(message, EngineName, EngineVersion, + originalScriptException) + { + CallStack = callStack + }; + } + wrapperScriptException.Type = type; + wrapperScriptException.DocumentName = documentName; + wrapperScriptException.LineNumber = lineNumber; + wrapperScriptException.ColumnNumber = columnNumber; + wrapperScriptException.SourceFragment = sourceFragment; - try + wrapperException = wrapperScriptException; + } + else + { + if (originalException is OriginalUsageException) { - result = bestFitConstructor.Invoke(processedArgs); + wrapperException = new WrapperUsageException(message, EngineName, EngineVersion, + originalException); } - catch (Exception e) + else if (originalException is OriginalEngineException) { - JsValue errorValue = JsErrorHelpers.CreateError( - string.Format(Strings.Runtime_HostTypeConstructorInvocationFailed, typeName, e.Message)); - JsErrorHelpers.SetException(errorValue); - - return undefinedValue; + wrapperException = new WrapperEngineException(message, EngineName, EngineVersion, + originalException); } + else if (originalException is OriginalFatalException) + { + wrapperException = new WrapperFatalException(message, EngineName, EngineVersion, + originalException); + } + else + { + wrapperException = new WrapperException(message, EngineName, EngineVersion, + originalException); + } + } - resultValue = MapToScriptType(result); - - return resultValue; - }; - _nativeFunctions.Add(nativeFunction); - - JsValue constructorValue = JsValue.CreateFunction(nativeFunction); + wrapperException.Description = description; - return constructorValue; + return wrapperException; } - private void ProjectFields(JsValue target, Type type, bool instance) + private static WrapperEngineLoadException WrapDllNotFoundException( + DllNotFoundException originalDllNotFoundException) { - string typeName = type.FullName; - BindingFlags defaultBindingFlags = ReflectionHelpers.GetDefaultBindingFlags(instance); - FieldInfo[] fields = type.GetFields(defaultBindingFlags); + string originalMessage = originalDllNotFoundException.Message; + string description; + string message; + bool isMonoRuntime = Utils.IsMonoRuntime(); - foreach (FieldInfo field in fields) + if ((isMonoRuntime && originalMessage == DllName.Universal) + || originalMessage.ContainsQuotedValue(DllName.Universal)) { - string fieldName = field.Name; - - JsValue descriptorValue = JsValue.CreateObject(); - descriptorValue.SetProperty("enumerable", JsValue.True, true); - - JsNativeFunction nativeGetFunction = (callee, isConstructCall, args, argCount, callbackData) => + const string buildInstructionsUrl = + "https://github.com/Microsoft/ChakraCore/wiki/Building-ChakraCore#{0}"; + const string manualInstallationInstructionsUrl = + "https://github.com/Taritsyn/JavaScriptEngineSwitcher/wiki/ChakraCore#{0}"; + Architecture osArchitecture = RuntimeInformation.OSArchitecture; + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder descriptionBuilder = stringBuilderPool.Rent(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - JsValue thisValue = args[0]; - JsValue undefinedValue = JsValue.Undefined; - - object thisObj = null; - - if (instance) + descriptionBuilder.AppendFormat(CoreStrings.Engine_AssemblyNotFound, DllName.ForWindows); + descriptionBuilder.Append(" "); + if (osArchitecture == Architecture.X64 || osArchitecture == Architecture.X86) { - if (!thisValue.HasExternalData) - { - JsValue errorValue = JsErrorHelpers.CreateTypeError( - string.Format(Strings.Runtime_InvalidThisContextForHostObjectField, fieldName)); - JsErrorHelpers.SetException(errorValue); - - return undefinedValue; - } - - thisObj = MapToHostType(thisValue); + descriptionBuilder.AppendFormat(CoreStrings.Engine_NuGetPackageInstallationRequired, + Utils.Is64BitProcess() ? + "JavaScriptEngineSwitcher.ChakraCore.Native.win-x64" + : + "JavaScriptEngineSwitcher.ChakraCore.Native.win-x86" + ); } - - object result; - - try + else if (osArchitecture == Architecture.Arm64 || osArchitecture == Architecture.Arm) { - result = field.GetValue(thisObj); + descriptionBuilder.AppendFormat(CoreStrings.Engine_NuGetPackageInstallationRequired, + Utils.Is64BitProcess() ? + "JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64" + : + "JavaScriptEngineSwitcher.ChakraCore.Native.win-arm" + ); } - catch (Exception e) + else { - string errorMessage = instance ? - string.Format(Strings.Runtime_HostObjectFieldGettingFailed, fieldName, e.Message) - : - string.Format(Strings.Runtime_HostTypeFieldGettingFailed, fieldName, typeName, e.Message) - ; - - JsValue errorValue = JsErrorHelpers.CreateError(errorMessage); - JsErrorHelpers.SetException(errorValue); - - return undefinedValue; + descriptionBuilder.AppendFormat(CoreStrings.Engine_NoNuGetPackageForProcessorArchitecture, + "JavaScriptEngineSwitcher.ChakraCore.Native.win*", + osArchitecture.ToString().ToLowerInvariant() + ); + descriptionBuilder.Append(" "); + descriptionBuilder.AppendFormat(Strings.Engine_BuildNativeAssemblyForCurrentProcessorArchitecture, + DllName.ForWindows, + string.Format(buildInstructionsUrl, "windows") + ); } - - JsValue resultValue = MapToScriptType(result); - - return resultValue; - }; - _nativeFunctions.Add(nativeGetFunction); - - JsValue getMethodValue = JsValue.CreateFunction(nativeGetFunction); - descriptorValue.SetProperty("get", getMethodValue, true); - - JsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) => + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - JsValue thisValue = args[0]; - JsValue undefinedValue = JsValue.Undefined; - - object thisObj = null; - - if (instance) + descriptionBuilder.AppendFormat(CoreStrings.Engine_AssemblyNotFound, DllName.ForLinux); + descriptionBuilder.Append(" "); + if (isMonoRuntime) + { + descriptionBuilder.AppendFormat(Strings.Engine_ManualInstallationUnderMonoRequired, + "JavaScriptEngineSwitcher.ChakraCore.Native.linux-*", + string.Format(manualInstallationInstructionsUrl, "linux") + ); + } + else { - if (!thisValue.HasExternalData) + if (osArchitecture == Architecture.X64) { - JsValue errorValue = JsErrorHelpers.CreateTypeError( - string.Format(Strings.Runtime_InvalidThisContextForHostObjectField, fieldName)); - JsErrorHelpers.SetException(errorValue); - - return undefinedValue; + descriptionBuilder.AppendFormat(CoreStrings.Engine_NuGetPackageInstallationRequired, + "JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64"); + } + else + { + descriptionBuilder.AppendFormat(CoreStrings.Engine_NoNuGetPackageForProcessorArchitecture, + "JavaScriptEngineSwitcher.ChakraCore.Native.linux-*", + osArchitecture.ToString().ToLowerInvariant() + ); + descriptionBuilder.Append(" "); + descriptionBuilder.AppendFormat(Strings.Engine_BuildNativeAssemblyForCurrentProcessorArchitecture, + DllName.ForLinux, + string.Format(buildInstructionsUrl, "linux") + ); } - - thisObj = MapToHostType(thisValue); } - - object value = MapToHostType(args.Skip(1).First()); - ReflectionHelpers.FixFieldValueType(ref value, field); - - try + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + descriptionBuilder.AppendFormat(CoreStrings.Engine_AssemblyNotFound, DllName.ForOsx); + descriptionBuilder.Append(" "); + if (isMonoRuntime) { - field.SetValue(thisObj, value); + descriptionBuilder.AppendFormat(Strings.Engine_ManualInstallationUnderMonoRequired, + "JavaScriptEngineSwitcher.ChakraCore.Native.osx-*", + string.Format(manualInstallationInstructionsUrl, "os-x") + ); } - catch (Exception e) + else { - string errorMessage = instance ? - string.Format(Strings.Runtime_HostObjectFieldSettingFailed, fieldName, e.Message) - : - string.Format(Strings.Runtime_HostTypeFieldSettingFailed, fieldName, typeName, e.Message) - ; + if (osArchitecture == Architecture.X64) + { + descriptionBuilder.AppendFormat(CoreStrings.Engine_NuGetPackageInstallationRequired, + "JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64"); + } + else + { + descriptionBuilder.AppendFormat(CoreStrings.Engine_NoNuGetPackageForProcessorArchitecture, + "JavaScriptEngineSwitcher.ChakraCore.Native.osx-*", + osArchitecture.ToString().ToLowerInvariant() + ); + descriptionBuilder.Append(" "); + descriptionBuilder.AppendFormat(Strings.Engine_BuildNativeAssemblyForCurrentProcessorArchitecture, + DllName.ForOsx, + string.Format(buildInstructionsUrl, "os-x") + ); + } + } + } + else + { + descriptionBuilder.Append(CoreStrings.Engine_OperatingSystemNotSupported); + } - JsValue errorValue = JsErrorHelpers.CreateError(errorMessage); - JsErrorHelpers.SetException(errorValue); + description = descriptionBuilder.ToString(); + stringBuilderPool.Return(descriptionBuilder); - return undefinedValue; - } + message = CoreErrorHelpers.GenerateEngineLoadErrorMessage(description, EngineName); + } + else + { + description = originalMessage; + message = CoreErrorHelpers.GenerateEngineLoadErrorMessage(description, EngineName, true); + } - return undefinedValue; - }; - _nativeFunctions.Add(nativeSetFunction); + var wrapperEngineLoadException = new WrapperEngineLoadException(message, EngineName, EngineVersion, + originalDllNotFoundException) + { + Description = description + }; - JsValue setMethodValue = JsValue.CreateFunction(nativeSetFunction); - descriptorValue.SetProperty("set", setMethodValue, true); + return wrapperEngineLoadException; + } - target.DefineProperty(fieldName, descriptorValue); - } + #endregion + + #region JsEngineBase overrides + + protected override IPrecompiledScript InnerPrecompile(string code) + { + return InnerPrecompile(code, null); } - private void ProjectProperties(JsValue target, Type type, bool instance) + protected override IPrecompiledScript InnerPrecompile(string code, string documentName) { - string typeName = type.FullName; - BindingFlags defaultBindingFlags = ReflectionHelpers.GetDefaultBindingFlags(instance); - PropertyInfo[] properties = type.GetProperties(defaultBindingFlags); + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); - foreach (PropertyInfo property in properties) + IPrecompiledScript precompiledScript = _dispatcher.Invoke(() => { - string propertyName = property.Name; - - JsValue descriptorValue = JsValue.CreateObject(); - descriptorValue.SetProperty("enumerable", JsValue.True, true); - - if (property.GetGetMethod() != null) + using (new JsScope(_jsContext)) { - JsNativeFunction nativeFunction = (callee, isConstructCall, args, argCount, callbackData) => + try { - JsValue thisValue = args[0]; - JsValue undefinedValue = JsValue.Undefined; - - object thisObj = null; + JsParseScriptAttributes parseAttributes = JsParseScriptAttributes.None; + byte[] cachedBytes = JsContext.SerializeScript(code, ref parseAttributes); - if (instance) - { - if (!thisValue.HasExternalData) - { - JsValue errorValue = JsErrorHelpers.CreateTypeError( - string.Format(Strings.Runtime_InvalidThisContextForHostObjectProperty, propertyName)); - JsErrorHelpers.SetException(errorValue); - - return undefinedValue; - } - - thisObj = MapToHostType(thisValue); - } - - object result; - - try - { - result = property.GetValue(thisObj, new object[0]); - } - catch (Exception e) - { - string errorMessage = instance ? - string.Format( - Strings.Runtime_HostObjectPropertyGettingFailed, propertyName, e.Message) - : - string.Format( - Strings.Runtime_HostTypePropertyGettingFailed, propertyName, typeName, e.Message) - ; - - JsValue errorValue = JsErrorHelpers.CreateError(errorMessage); - JsErrorHelpers.SetException(errorValue); - - return undefinedValue; - } - - JsValue resultValue = MapToScriptType(result); - - return resultValue; - }; - _nativeFunctions.Add(nativeFunction); - - JsValue getMethodValue = JsValue.CreateFunction(nativeFunction); - descriptorValue.SetProperty("get", getMethodValue, true); - } - - if (property.GetSetMethod() != null) - { - JsNativeFunction nativeFunction = (callee, isConstructCall, args, argCount, callbackData) => + return new ChakraCorePrecompiledScript(code, parseAttributes, cachedBytes, uniqueDocumentName); + } + catch (OriginalException e) { - JsValue thisValue = args[0]; - JsValue undefinedValue = JsValue.Undefined; - - object thisObj = null; - - if (instance) - { - if (!thisValue.HasExternalData) - { - JsValue errorValue = JsErrorHelpers.CreateTypeError( - string.Format(Strings.Runtime_InvalidThisContextForHostObjectProperty, propertyName)); - JsErrorHelpers.SetException(errorValue); - - return undefinedValue; - } - - thisObj = MapToHostType(thisValue); - } - - object value = MapToHostType(args.Skip(1).First()); - ReflectionHelpers.FixPropertyValueType(ref value, property); - - try - { - property.SetValue(thisObj, value, new object[0]); - } - catch (Exception e) - { - string errorMessage = instance ? - string.Format( - Strings.Runtime_HostObjectPropertySettingFailed, propertyName, e.Message) - : - string.Format( - Strings.Runtime_HostTypePropertySettingFailed, propertyName, typeName, e.Message) - ; - - JsValue errorValue = JsErrorHelpers.CreateError(errorMessage); - JsErrorHelpers.SetException(errorValue); - - return undefinedValue; - } - - return undefinedValue; - }; - _nativeFunctions.Add(nativeFunction); - - JsValue setMethodValue = JsValue.CreateFunction(nativeFunction); - descriptorValue.SetProperty("set", setMethodValue, true); + throw WrapJsException(e, uniqueDocumentName); + } } + }); - target.DefineProperty(propertyName, descriptorValue); - } + return precompiledScript; + } + + protected override object InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); } - private void ProjectMethods(JsValue target, Type type, bool instance) + protected override object InnerEvaluate(string expression, string documentName) { - string typeName = type.FullName; - BindingFlags defaultBindingFlags = ReflectionHelpers.GetDefaultBindingFlags(instance); - MethodInfo[] methods = type.GetMethods(defaultBindingFlags); - IEnumerable> methodGroups = methods.GroupBy(m => m.Name); + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); - foreach (IGrouping methodGroup in methodGroups) + object result = _dispatcher.Invoke(() => { - string methodName = methodGroup.Key; - MethodInfo[] methodCandidates = methodGroup.ToArray(); - - JsNativeFunction nativeFunction = (callee, isConstructCall, args, argCount, callbackData) => + using (new JsScope(_jsContext)) { - JsValue thisValue = args[0]; - JsValue undefinedValue = JsValue.Undefined; - - object thisObj = null; - - if (instance) + try { - if (!thisValue.HasExternalData) - { - JsValue errorValue = JsErrorHelpers.CreateTypeError( - string.Format(Strings.Runtime_InvalidThisContextForHostObjectMethod, methodName)); - JsErrorHelpers.SetException(errorValue); + JsParseScriptAttributes parseAttributes = JsParseScriptAttributes.None; + JsValue resultValue = JsContext.RunScript(expression, _jsSourceContext++, + uniqueDocumentName, ref parseAttributes); - return undefinedValue; - } - - thisObj = MapToHostType(thisValue); + return _typeMapper.MapToHostType(resultValue); } - - object[] processedArgs = MapToHostType(args.Skip(1).ToArray()); - - var bestFitMethod = (MethodInfo)ReflectionHelpers.GetBestFitMethod( - methodCandidates, processedArgs); - if (bestFitMethod == null) + catch (OriginalException e) { - JsValue errorValue = JsErrorHelpers.CreateReferenceError( - string.Format(Strings.Runtime_SuitableMethodOfHostObjectNotFound, methodName)); - JsErrorHelpers.SetException(errorValue); - - return undefinedValue; + throw WrapJsException(e); } + } + }); - ReflectionHelpers.FixArgumentTypes(ref processedArgs, bestFitMethod.GetParameters()); - - object result; - - try - { - result = bestFitMethod.Invoke(thisObj, processedArgs); - } - catch (Exception e) - { - string errorMessage = instance ? - string.Format( - Strings.Runtime_HostObjectMethodInvocationFailed, methodName, e.Message) - : - string.Format( - Strings.Runtime_HostTypeMethodInvocationFailed, methodName, typeName, e.Message) - ; - - JsValue errorValue = JsErrorHelpers.CreateError(errorMessage); - JsErrorHelpers.SetException(errorValue); + return result; + } - return undefinedValue; - } + protected override T InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } - JsValue resultValue = MapToScriptType(result); + protected override T InnerEvaluate(string expression, string documentName) + { + object result = InnerEvaluate(expression, documentName); - return resultValue; - }; - _nativeFunctions.Add(nativeFunction); + return TypeConverter.ConvertToType(result); + } - JsValue methodValue = JsValue.CreateFunction(nativeFunction); - target.SetProperty(methodName, methodValue, true); - } + protected override void InnerExecute(string code) + { + InnerExecute(code, null); } - private static JsRuntimeException ConvertJsExceptionToJsRuntimeException( - OriginalJsException jsException) + protected override void InnerExecute(string code, string documentName) { - string message = jsException.Message; - string category = string.Empty; - int lineNumber = 0; - int columnNumber = 0; - string sourceFragment = string.Empty; + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); - var jsScriptException = jsException as JsScriptException; - if (jsScriptException != null) + _dispatcher.Invoke(() => { - category = "Script error"; - JsValue errorValue = jsScriptException.Error; - - JsValue messagePropertyValue = errorValue.GetProperty("message"); - string scriptMessage = messagePropertyValue.ConvertToString().ToString(); - if (!string.IsNullOrWhiteSpace(scriptMessage)) + using (new JsScope(_jsContext)) { - message = string.Format("{0}: {1}", message.TrimEnd('.'), scriptMessage); - } - - JsPropertyId linePropertyId = JsPropertyId.FromString("line"); - if (errorValue.HasProperty(linePropertyId)) - { - JsValue linePropertyValue = errorValue.GetProperty(linePropertyId); - lineNumber = (int)linePropertyValue.ConvertToNumber().ToDouble() + 1; - } - - JsPropertyId columnPropertyId = JsPropertyId.FromString("column"); - if (errorValue.HasProperty(columnPropertyId)) - { - JsValue columnPropertyValue = errorValue.GetProperty(columnPropertyId); - columnNumber = (int)columnPropertyValue.ConvertToNumber().ToDouble() + 1; + try + { + JsParseScriptAttributes parseAttributes = JsParseScriptAttributes.None; + JsContext.RunScript(code, _jsSourceContext++, uniqueDocumentName, ref parseAttributes); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } } + }); + } - JsPropertyId sourcePropertyId = JsPropertyId.FromString("source"); - if (errorValue.HasProperty(sourcePropertyId)) - { - JsValue sourcePropertyValue = errorValue.GetProperty(sourcePropertyId); - sourceFragment = sourcePropertyValue.ConvertToString().ToString(); - } - } - else if (jsException is JsUsageException) - { - category = "Usage error"; - } - else if (jsException is JsEngineException) - { - category = "Engine error"; - } - else if (jsException is JsFatalException) + protected override void InnerExecute(IPrecompiledScript precompiledScript) + { + var chakraCorePrecompiledScript = precompiledScript as ChakraCorePrecompiledScript; + if (chakraCorePrecompiledScript == null) { - category = "Fatal error"; + throw new WrapperUsageException( + string.Format(CoreStrings.Usage_CannotConvertPrecompiledScriptToInternalType, + typeof(ChakraCorePrecompiledScript).FullName), + Name, Version + ); } - var jsEngineException = new JsRuntimeException(message, ENGINE_NAME, ENGINE_VERSION) + _dispatcher.Invoke(() => { - ErrorCode = ((uint)jsException.ErrorCode).ToString(CultureInfo.InvariantCulture), - Category = category, - LineNumber = lineNumber, - ColumnNumber = columnNumber, - SourceFragment = sourceFragment, - HelpLink = jsException.HelpLink - }; - - return jsEngineException; + using (new JsScope(_jsContext)) + { + try + { + JsContext.RunSerializedScript(chakraCorePrecompiledScript.Code, + chakraCorePrecompiledScript.CachedBytes, + chakraCorePrecompiledScript.LoadScriptSourceCodeCallback, _jsSourceContext++, + chakraCorePrecompiledScript.DocumentName); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + finally + { + GC.KeepAlive(chakraCorePrecompiledScript); + } + } + }); } - #endregion - - #region JsEngineBase implementation - - protected override object InnerEvaluate(string expression) + protected override object InnerCallFunction(string functionName, params object[] args) { - object result = InvokeScript(() => + object result = _dispatcher.Invoke(() => { - JsValue resultValue = JsContext.RunScript(expression); + using (new JsScope(_jsContext)) + { + try + { + JsValue globalObj = JsValue.GlobalObject; + JsPropertyId functionId = JsPropertyId.FromString(functionName); - return MapToHostType(resultValue); - }); + bool functionExist = globalObj.HasProperty(functionId); + if (!functionExist) + { + throw new WrapperRuntimeException( + string.Format(CoreStrings.Runtime_FunctionNotExist, functionName), + EngineName, EngineVersion + ); + } - return result; - } + JsValue resultValue; + JsValue functionValue = globalObj.GetProperty(functionId); - protected override T InnerEvaluate(string expression) - { - object result = InnerEvaluate(expression); + int argCount = args.Length; + if (argCount > 0) + { + int processedArgCount = argCount + 1; + var processedArgs = new JsValue[processedArgCount]; + processedArgs[0] = globalObj; - return TypeConverter.ConvertToType(result); - } + for (int argIndex = 0; argIndex < argCount; argIndex++) + { + JsValue processedArg = _typeMapper.MapToScriptType(args[argIndex]); + AddReferenceToValue(processedArg); - protected override void InnerExecute(string code) - { - InvokeScript(() => JsContext.RunScript(code)); - } + processedArgs[argIndex + 1] = processedArg; + } - protected override object InnerCallFunction(string functionName, params object[] args) - { - object result = InvokeScript(() => - { - JsValue globalObj = JsValue.GlobalObject; - JsPropertyId functionId = JsPropertyId.FromString(functionName); + try + { + resultValue = functionValue.CallFunction(processedArgs); + } + finally + { + for (int argIndex = 1; argIndex < processedArgCount; argIndex++) + { + RemoveReferenceToValue(processedArgs[argIndex]); + } + } + } + else + { + resultValue = functionValue.CallFunction(globalObj); + } - bool functionExist = globalObj.HasProperty(functionId); - if (!functionExist) - { - throw new JsRuntimeException( - string.Format(CoreStrings.Runtime_FunctionNotExist, functionName)); + return _typeMapper.MapToHostType(resultValue); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } } - - var processedArgs = MapToScriptType(args); - var allProcessedArgs = new[] { globalObj }.Concat(processedArgs).ToArray(); - - JsValue functionValue = globalObj.GetProperty(functionId); - JsValue resultValue = functionValue.CallFunction(allProcessedArgs); - - return MapToHostType(resultValue); }); return result; @@ -921,19 +890,29 @@ protected override T InnerCallFunction(string functionName, params object[] a protected override bool InnerHasVariable(string variableName) { - bool result = InvokeScript(() => + bool result = _dispatcher.Invoke(() => { - JsValue globalObj = JsValue.GlobalObject; - JsPropertyId variableId = JsPropertyId.FromString(variableName); - bool variableExist = globalObj.HasProperty(variableId); - - if (variableExist) + using (new JsScope(_jsContext)) { - JsValue variableValue = globalObj.GetProperty(variableId); - variableExist = variableValue.ValueType != JsValueType.Undefined; - } + try + { + JsValue globalObj = JsValue.GlobalObject; + JsPropertyId variableId = JsPropertyId.FromString(variableName); + bool variableExist = globalObj.HasProperty(variableId); + + if (variableExist) + { + JsValue variableValue = globalObj.GetProperty(variableId); + variableExist = variableValue.ValueType != JsValueType.Undefined; + } - return variableExist; + return variableExist; + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); return result; @@ -941,11 +920,21 @@ protected override bool InnerHasVariable(string variableName) protected override object InnerGetVariableValue(string variableName) { - object result = InvokeScript(() => + object result = _dispatcher.Invoke(() => { - JsValue variableValue = JsValue.GlobalObject.GetProperty(variableName); + using (new JsScope(_jsContext)) + { + try + { + JsValue variableValue = JsValue.GlobalObject.GetProperty(variableName); - return MapToHostType(variableValue); + return _typeMapper.MapToHostType(variableValue); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); return result; @@ -960,45 +949,131 @@ protected override T InnerGetVariableValue(string variableName) protected override void InnerSetVariableValue(string variableName, object value) { - InvokeScript(() => + _dispatcher.Invoke(() => { - JsValue inputValue = MapToScriptType(value); - JsValue.GlobalObject.SetProperty(variableName, inputValue, true); + using (new JsScope(_jsContext)) + { + try + { + JsValue inputValue = _typeMapper.MapToScriptType(value); + AddReferenceToValue(inputValue); + + try + { + JsValue.GlobalObject.SetProperty(variableName, inputValue, true); + } + finally + { + RemoveReferenceToValue(inputValue); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); } protected override void InnerRemoveVariable(string variableName) { - InvokeScript(() => + _dispatcher.Invoke(() => { - JsValue globalObj = JsValue.GlobalObject; - JsPropertyId variableId = JsPropertyId.FromString(variableName); - - if (globalObj.HasProperty(variableId)) + using (new JsScope(_jsContext)) { - globalObj.SetProperty(variableId, JsValue.Undefined, true); + try + { + JsValue globalObj = JsValue.GlobalObject; + JsPropertyId variableId = JsPropertyId.FromString(variableName); + + if (globalObj.HasProperty(variableId)) + { + globalObj.SetProperty(variableId, JsValue.Undefined, true); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } } }); } protected override void InnerEmbedHostObject(string itemName, object value) { - InvokeScript(() => + _dispatcher.Invoke(() => { - JsValue processedValue = MapToScriptType(value); - JsValue.GlobalObject.SetProperty(itemName, processedValue, true); + using (new JsScope(_jsContext)) + { + try + { + JsValue processedValue = _typeMapper.GetOrCreateScriptObject(value); + JsValue.GlobalObject.SetProperty(itemName, processedValue, true); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); } protected override void InnerEmbedHostType(string itemName, Type type) { - InvokeScript(() => + _dispatcher.Invoke(() => { - JsValue typeValue = CreateObjectFromType(type); - JsValue.GlobalObject.SetProperty(itemName, typeValue, true); + using (new JsScope(_jsContext)) + { + try + { + JsValue typeValue = _typeMapper.GetOrCreateScriptType(type); + JsValue.GlobalObject.SetProperty(itemName, typeValue, true); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); } + protected override void InnerInterrupt() + { + _jsRuntime.Disabled = true; + } + + protected override void InnerCollectGarbage() + { + _jsRuntime.CollectGarbage(); + } + + #region IJsEngine implementation + + public override string Name + { + get { return EngineName; } + } + + public override string Version + { + get { return EngineVersion; } + } + + public override bool SupportsScriptPrecompilation + { + get { return true; } + } + + public override bool SupportsScriptInterruption + { + get { return true; } + } + + public override bool SupportsGarbageCollection + { + get { return true; } + } + #endregion #region IDisposable implementation @@ -1012,6 +1087,53 @@ public override void Dispose() GC.SuppressFinalize(this); } + + /// + /// Destroys object + /// + /// Flag, allowing destruction of + /// managed objects contained in fields of class + private void Dispose(bool disposing) + { + if (disposing) + { + if (_disposedFlag.Set()) + { + if (_dispatcher != null) + { + _dispatcher.Invoke(DisposeUnmanagedResources); + + _dispatcher.Dispose(); + _dispatcher = null; + } + + if (_typeMapper != null) + { + _typeMapper.Dispose(); + _typeMapper = null; + } + + _documentNameManager = null; + _promiseContinuationCallback = null; + } + } + else + { + DisposeUnmanagedResources(); + } + } + + private void DisposeUnmanagedResources() + { + if (_jsContext.IsValid) + { + _jsContext.Release(); + } + _jsRuntime.Dispose(); + } + + #endregion + #endregion } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreJsEngineFactory.cs b/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreJsEngineFactory.cs new file mode 100644 index 00000000..c85357ef --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreJsEngineFactory.cs @@ -0,0 +1,53 @@ +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.ChakraCore +{ + /// + /// ChakraCore JS engine factory + /// + public sealed class ChakraCoreJsEngineFactory : IJsEngineFactory + { + /// + /// Settings of the ChakraCore JS engine + /// + private readonly ChakraCoreSettings _settings; + + + /// + /// Constructs an instance of the ChakraCore JS engine factory + /// + public ChakraCoreJsEngineFactory() + : this(new ChakraCoreSettings()) + { } + + /// + /// Constructs an instance of the ChakraCore JS engine factory + /// + /// Settings of the ChakraCore JS engine + public ChakraCoreJsEngineFactory(ChakraCoreSettings settings) + { + _settings = settings; + } + + + #region IJsEngineFactory implementation + + /// + public string EngineName + { + get { return ChakraCoreJsEngine.EngineName; } + } + + + /// + /// Creates a instance of the ChakraCore JS engine + /// + /// Instance of the ChakraCore JS engine + public IJsEngine CreateEngine() + { + return new ChakraCoreJsEngine(_settings); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCorePrecompiledScript.cs b/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCorePrecompiledScript.cs new file mode 100644 index 00000000..f9bf6583 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCorePrecompiledScript.cs @@ -0,0 +1,160 @@ +using System.Text; + +using JavaScriptEngineSwitcher.Core; + +using JavaScriptEngineSwitcher.ChakraCore.JsRt; +using OriginalException = JavaScriptEngineSwitcher.ChakraCore.JsRt.JsException; + +namespace JavaScriptEngineSwitcher.ChakraCore +{ + /// + /// Represents a pre-compiled script that can be executed by different instances of the ChakraCore JS engine + /// + internal sealed class ChakraCorePrecompiledScript : IPrecompiledScript + { + /// + /// Source code of the script + /// + private readonly string _code; + + /// + /// Attribute mask for parsing the script + /// + private readonly JsParseScriptAttributes _parseAttributes; + + /// + /// Cached data for accelerated recompilation + /// + private readonly byte[] _cachedBytes; + + /// + /// Document name + /// + private readonly string _documentName; + + /// + /// Callback to load the source code of the serialized script + /// + private readonly JsSerializedLoadScriptCallback _loadScriptSourceCodeCallback; + + /// + /// Source code of the script as an array of bytes + /// + private byte[] _codeBytes; + + /// + /// Synchronizer of the script source code loading + /// + private readonly object _scriptLoadingSynchronizer = new object(); + + /// + /// Gets a source code of the script + /// + public string Code + { + get { return _code; } + } + + /// + /// Gets a attribute mask for parsing the script + /// + public JsParseScriptAttributes ParseAttributes + { + get { return _parseAttributes; } + } + + /// + /// Gets a cached data for accelerated recompilation + /// + public byte[] CachedBytes + { + get { return _cachedBytes; } + } + + /// + /// Gets a document name + /// + public string DocumentName + { + get { return _documentName; } + } + + /// + /// Gets a callback to load the source code of the serialized script + /// + public JsSerializedLoadScriptCallback LoadScriptSourceCodeCallback + { + get { return _loadScriptSourceCodeCallback; } + } + + + /// + /// Constructs an instance of pre-compiled script + /// + /// The source code of the script + /// Attribute mask for parsing the script + /// Cached data for accelerated recompilation + /// Document name + public ChakraCorePrecompiledScript(string code, JsParseScriptAttributes parseAttributes, byte[] cachedBytes, + string documentName) + { + _code = code; + _parseAttributes = parseAttributes; + _cachedBytes = cachedBytes; + _documentName = documentName; + _loadScriptSourceCodeCallback = LoadScriptSourceCode; + } + + + /// + /// Loads a source code of the serialized script + /// + /// A cookie identifying the script that can be used + /// by debuggable script contexts + /// The script returned + /// Attribute mask for parsing the script + /// true if the operation succeeded, false otherwise + private bool LoadScriptSourceCode(JsSourceContext sourceContext, out JsValue value, + out JsParseScriptAttributes parseAttributes) + { + if (_codeBytes == null) + { + lock (_scriptLoadingSynchronizer) + { + if (_codeBytes == null) + { + Encoding encoding = _parseAttributes.HasFlag(JsParseScriptAttributes.ArrayBufferIsUtf16Encoded) ? + Encoding.Unicode : Encoding.UTF8; + _codeBytes = encoding.GetBytes(_code); + } + } + } + + bool result; + parseAttributes = _parseAttributes; + + try + { + value = JsValue.CreateExternalArrayBuffer(_codeBytes); + result = true; + } + catch (OriginalException) + { + value = JsValue.Invalid; + result = false; + } + + return result; + } + + #region IPrecompiledScript implementation + + /// + public string EngineName + { + get { return ChakraCoreJsEngine.EngineName; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreSettings.cs b/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreSettings.cs new file mode 100644 index 00000000..80b05170 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreSettings.cs @@ -0,0 +1,166 @@ +using System; + +using JavaScriptEngineSwitcher.Core.Utilities; + +using JavaScriptEngineSwitcher.ChakraCore.Resources; + +namespace JavaScriptEngineSwitcher.ChakraCore +{ + /// + /// Settings of the ChakraCore JS engine + /// + public sealed class ChakraCoreSettings + { +#if !NETSTANDARD1_3 + /// + /// The stack size is sufficient to run the code of modern JavaScript libraries in 32-bit process + /// + const int STACK_SIZE_32 = 492 * 1024; // like 32-bit Node.js + + /// + /// The stack size is sufficient to run the code of modern JavaScript libraries in 64-bit process + /// + const int STACK_SIZE_64 = 984 * 1024; // like 64-bit Node.js + + /// + /// The maximum stack size in bytes + /// + private int _maxStackSize; + +#endif + /// + /// Gets or sets a flag for whether to allow the usage of reflection API in the script code + /// + /// + /// This affects , Exception.GetType, + /// Exception.TargetSite and Delegate.Method. + /// + public bool AllowReflection + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to disable any background work (such as garbage collection) + /// on background threads + /// + public bool DisableBackgroundWork + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to disable calls of eval function with custom code + /// and Function constructors taking function code as string + /// + public bool DisableEval + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to disable executable page allocation + /// + /// + /// + /// This also implies that Native Code generation will be turned off. + /// + /// + /// Note that this will break JavaScript stack decoding in tools like WPA since they + /// rely on allocation of unique thunks to interpret each function and allocation of + /// those thunks will be disabled as well. + /// + /// + public bool DisableExecutablePageAllocation + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to disable Failfast fatal error on OOM + /// + public bool DisableFatalOnOOM + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to disable native code generation + /// + public bool DisableNativeCodeGeneration + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to enable all experimental features + /// + public bool EnableExperimentalFeatures + { + get; + set; + } +#if !NETSTANDARD1_3 + + /// + /// Gets or sets a maximum stack size in bytes + /// + /// + /// Set a 0 to use the default maximum stack size specified in the header + /// for the executable. + /// + public int MaxStackSize + { + get { return _maxStackSize; } + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException( + nameof(value), + Strings.Engine_MaxStackSizeMustBeNonNegative + ); + } + + _maxStackSize = value; + } + } +#endif + + /// + /// Gets or sets a current memory limit for a runtime in bytes + /// + public UIntPtr MemoryLimit + { + get; + set; + } + + + /// + /// Constructs an instance of the ChakraCore settings + /// + public ChakraCoreSettings() + { + bool is64BitProcess = Utils.Is64BitProcess(); + + AllowReflection = false; + DisableBackgroundWork = false; + DisableEval = false; + DisableExecutablePageAllocation = false; + DisableFatalOnOOM = true; + DisableNativeCodeGeneration = false; + EnableExperimentalFeatures = false; +#if !NETSTANDARD1_3 + MaxStackSize = is64BitProcess ? STACK_SIZE_64 : STACK_SIZE_32; +#endif + MemoryLimit = is64BitProcess ? new UIntPtr(ulong.MaxValue) : new UIntPtr(uint.MaxValue); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/Constants/DllName.cs b/src/JavaScriptEngineSwitcher.ChakraCore/Constants/DllName.cs new file mode 100644 index 00000000..9160e537 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/Constants/DllName.cs @@ -0,0 +1,13 @@ +namespace JavaScriptEngineSwitcher.ChakraCore.Constants +{ + /// + /// DLL names + /// + internal static class DllName + { + public const string Universal = "ChakraCore"; + public const string ForWindows = Universal + ".dll"; + public const string ForLinux = "lib" + Universal + ".so"; + public const string ForOsx = "lib" + Universal + ".dylib"; + } +} diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/Helpers/EncodingHelpers.cs b/src/JavaScriptEngineSwitcher.ChakraCore/Helpers/EncodingHelpers.cs new file mode 100644 index 00000000..31eb06e1 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/Helpers/EncodingHelpers.cs @@ -0,0 +1,100 @@ +#if NET45_OR_GREATER || NETSTANDARD +using System.Buffers; +#endif +using System.Text; +#if NET40 + +using PolyfillsForOldDotNet.System.Buffers; +#endif + +namespace JavaScriptEngineSwitcher.ChakraCore.Helpers +{ + /// + /// Encoding helpers + /// + internal static class EncodingHelpers + { + public static string UnicodeToAnsi(string value, out int byteCount) + { + if (string.IsNullOrEmpty(value)) + { + byteCount = 0; + return value; + } + + string result; + int valueLength = value.Length; + Encoding utf8Encoding = Encoding.UTF8; +#if NETFRAMEWORK + Encoding ansiEncoding = Encoding.Default; +#else + Encoding ansiEncoding = Encoding.GetEncoding(0); +#endif + + var byteArrayPool = ArrayPool.Shared; + int bufferLength = utf8Encoding.GetByteCount(value); + byte[] buffer = byteArrayPool.Rent(bufferLength + 1); + buffer[bufferLength] = 0; + + try + { +#if NET45_OR_GREATER || NETSTANDARD + result = ConvertStringInternal(utf8Encoding, ansiEncoding, value, valueLength, buffer, bufferLength); +#else + utf8Encoding.GetBytes(value, 0, valueLength, buffer, 0); + result = ansiEncoding.GetString(buffer, 0, bufferLength); +#endif + } + finally + { + byteArrayPool.Return(buffer); + } + + byteCount = bufferLength; + + return result; + } +#if NET45_OR_GREATER || NETSTANDARD + + private static unsafe string ConvertStringInternal(Encoding srcEncoding, Encoding dstEncoding, string s, + int charCount, byte[] bytes, int byteCount) + { + fixed (char* pString = s) + fixed (byte* pBytes = bytes) + { + srcEncoding.GetBytes(pString, charCount, pBytes, byteCount); +#if NET471 || NETSTANDARD + string result = dstEncoding.GetString(pBytes, byteCount); + + return result; + } +#else + } + + int resultLength = dstEncoding.GetCharCount(bytes, 0, byteCount); + var charArrayPool = ArrayPool.Shared; + char[] resultChars = charArrayPool.Rent(resultLength + 1); + resultChars[resultLength] = '\0'; + + string result; + + try + { + fixed (byte* pBytes = bytes) + fixed (char* pResultChars = resultChars) + { + dstEncoding.GetChars(pBytes, byteCount, pResultChars, resultLength); + result = new string(pResultChars, 0, resultLength); + } + } + finally + { + charArrayPool.Return(resultChars); + } + + return result; +#endif + } +#endif + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/Helpers/NumericHelpers.cs b/src/JavaScriptEngineSwitcher.ChakraCore/Helpers/NumericHelpers.cs index 7039e484..4887e856 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/Helpers/NumericHelpers.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/Helpers/NumericHelpers.cs @@ -1,7 +1,9 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.Helpers -{ - using System; +using System; + +using JavaScriptEngineSwitcher.Core.Extensions; +namespace JavaScriptEngineSwitcher.ChakraCore.Helpers +{ /// /// Numeric helpers /// @@ -14,10 +16,10 @@ internal static class NumericHelpers /// Gets a value indicating whether the specified type is one of the numeric types /// /// The type - /// true if the specified type is one of the numeric types; otherwise, false + /// true if the specified type is one of the numeric types; otherwise, false public static bool IsNumericType(Type type) { - TypeCode typeCode = Type.GetTypeCode(type); + TypeCode typeCode = type.GetTypeCode(); switch (typeCode) { diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/Helpers/ReflectionHelpers.cs b/src/JavaScriptEngineSwitcher.ChakraCore/Helpers/ReflectionHelpers.cs index 3f0ce1c0..91099f72 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/Helpers/ReflectionHelpers.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/Helpers/ReflectionHelpers.cs @@ -1,16 +1,30 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.Helpers -{ - using System; - using System.Linq; - using System.Reflection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; - using Core.Utilities; +using JavaScriptEngineSwitcher.Core.Utilities; +namespace JavaScriptEngineSwitcher.ChakraCore.Helpers +{ /// /// Reflection helpers /// internal static class ReflectionHelpers { + private static readonly PropertyInfo[] _disallowedProperties = + { + typeof(Delegate).GetProperty("Method"), + typeof(Exception).GetProperty("TargetSite") + }; + + private static readonly MethodInfo[] _disallowedMethods = + { + typeof(object).GetMethod("GetType"), + typeof(Exception).GetMethod("GetType") + }; + + public static BindingFlags GetDefaultBindingFlags(bool instance) { BindingFlags bindingFlags = BindingFlags.Public; @@ -26,8 +40,41 @@ public static BindingFlags GetDefaultBindingFlags(bool instance) return bindingFlags; } + public static bool IsAllowedProperty(PropertyInfo property) + { + bool isAllowed = !_disallowedProperties.Contains(property, MemberComparer.Instance); + + return isAllowed; + } + + public static bool IsAllowedMethod(MethodInfo method) + { + bool isAllowed = !_disallowedMethods.Contains(method, MemberComparer.Instance); + + return isAllowed; + } + + public static bool IsFullyFledgedMethod(MethodInfo method) + { + if (!method.Attributes.HasFlag(MethodAttributes.SpecialName)) + { + return true; + } + + string name = method.Name; + bool isFullyFledged = !(name.StartsWith("get_", StringComparison.Ordinal) + || name.StartsWith("set_", StringComparison.Ordinal)); + + return isFullyFledged; + } + public static void FixFieldValueType(ref object value, FieldInfo field) { + if (value == null) + { + return; + } + Type valueType = value.GetType(); Type fieldType = field.FieldType; @@ -44,6 +91,11 @@ public static void FixFieldValueType(ref object value, FieldInfo field) public static void FixPropertyValueType(ref object value, PropertyInfo property) { + if (value == null) + { + return; + } + Type valueType = value.GetType(); Type propertyType = property.PropertyType; @@ -61,10 +113,21 @@ public static void FixPropertyValueType(ref object value, PropertyInfo property) public static void FixArgumentTypes(ref object[] argValues, ParameterInfo[] parameters) { int argCount = argValues.Length; + int parameterCount = parameters.Length; for (int argIndex = 0; argIndex < argCount; argIndex++) { + if (argIndex >= parameterCount) + { + break; + } + object argValue = argValues[argIndex]; + if (argValue == null) + { + continue; + } + Type argType = argValue.GetType(); ParameterInfo parameter = parameters[argIndex]; @@ -84,9 +147,8 @@ public static void FixArgumentTypes(ref object[] argValues, ParameterInfo[] para public static MethodBase GetBestFitMethod(MethodBase[] methods, object[] argValues) { - int argCount = argValues.Length; - var methodCandidates = methods - .Select(m => new + MethodWithMetadata[] methodCandidates = methods + .Select(m => new MethodWithMetadata { Method = m, ParameterTypes = m.GetParameters() @@ -95,54 +157,62 @@ public static MethodBase GetBestFitMethod(MethodBase[] methods, object[] argValu }) .ToArray() ; - - var methodsWithSameArity = methodCandidates + int argCount = argValues.Length; + MethodWithMetadata[] sameArityMethods = methodCandidates .Where(m => m.ParameterTypes.Length == argCount) .ToArray() ; - if (methodsWithSameArity.Length == 0) + + int sameArityMethodCount = sameArityMethods.Length; + if (sameArityMethodCount == 0) { return null; } Type[] argTypes = argValues - .Select(a => a.GetType()) - .ToArray() - ; - var weaklyCompatibleMethods = methodsWithSameArity - .Where(m => CompareParameterTypes(argValues, argTypes, m.ParameterTypes, false)) + .Select(a => a != null ? a.GetType() : typeof(object)) .ToArray() ; + var compatibleMethods = new List(); - int weaklyCompatibleMethodCount = weaklyCompatibleMethods.Length; - if (weaklyCompatibleMethodCount > 0) + for (int methodIndex = 0; methodIndex < sameArityMethodCount; methodIndex++) { - if (weaklyCompatibleMethodCount == 1) + MethodWithMetadata method = sameArityMethods[methodIndex]; + ushort compatibilityScore; + + if (CompareParameterTypes(argValues, argTypes, method.ParameterTypes, out compatibilityScore)) { - return weaklyCompatibleMethods[0].Method; + method.CompatibilityScore = compatibilityScore; + compatibleMethods.Add(method); } + } - var strictlyCompatibleMethods = weaklyCompatibleMethods - .Where(m => CompareParameterTypes(argValues, argTypes, m.ParameterTypes, true)) - .ToArray() - ; - if (strictlyCompatibleMethods.Length > 0) + int compatibleMethodCount = compatibleMethods.Count; + if (compatibleMethodCount > 0) + { + if (compatibleMethodCount == 1) { - return strictlyCompatibleMethods[0].Method; + return compatibleMethods[0].Method; } - return weaklyCompatibleMethods[0].Method; + MethodWithMetadata bestFitMethod = compatibleMethods + .OrderByDescending(m => m.CompatibilityScore) + .First() + ; + + return bestFitMethod.Method; } return null; } private static bool CompareParameterTypes(object[] argValues, Type[] argTypes, Type[] parameterTypes, - bool strictСompliance) + out ushort compatibilityScore) { int argValueCount = argValues.Length; int argTypeCount = argTypes.Length; int parameterCount = parameterTypes.Length; + compatibilityScore = 0; if (argValueCount != argTypeCount || argTypeCount != parameterCount) { @@ -155,31 +225,90 @@ private static bool CompareParameterTypes(object[] argValues, Type[] argTypes, T Type argType = argTypes[argIndex]; Type parameterType = parameterTypes[argIndex]; - if (argType != parameterType) + if (argType == parameterType) { - if (!strictСompliance - && NumericHelpers.IsNumericType(argType) && NumericHelpers.IsNumericType(parameterType)) + compatibilityScore++; + } + else + { + // TODO: It is necessary to calculate the compatibility score based on length + // of inheritance and interface implementation chains. + object convertedArgValue; + + if (!TypeConverter.TryConvertToType(argValue, parameterType, out convertedArgValue)) { - object convertedArgValue; + return false; + } - if (!TypeConverter.TryConvertToType(argValue, parameterType, out convertedArgValue)) - { - return false; - } + continue; + } + } - if (argValue != convertedArgValue) - { - return false; - } + return true; + } - continue; - } + private sealed class MemberComparer : EqualityComparer + where T : MemberInfo + { + public static MemberComparer Instance { get; } = new MemberComparer(); + + + private MemberComparer() + { } + + + #region MemberComparer overrides + + public override bool Equals(T x, T y) + { + if (x == null && y == null) + { + return true; + } + else if (x == null || y == null) + { return false; } + + return x.Module == y.Module +#if !NETSTANDARD1_3 + && x.MetadataToken == y.MetadataToken +#else + && x.DeclaringType == y.DeclaringType + && x.Name == y.Name +#endif + ; } - return true; + public override int GetHashCode(T obj) + { + return obj != null ? obj.GetHashCode() : 0; + } + + #endregion + } + + private sealed class MethodWithMetadata + { + public MethodBase Method + { + get; + set; + } + + public Type[] ParameterTypes + { + get; + set; + } + + /// TODO: In future will need to change type to double + public ushort CompatibilityScore + { + get; + set; + } } } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JavaScriptEngineSwitcher.ChakraCore.csproj b/src/JavaScriptEngineSwitcher.ChakraCore/JavaScriptEngineSwitcher.ChakraCore.csproj index 4f5a9d0a..6ec88163 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JavaScriptEngineSwitcher.ChakraCore.csproj +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JavaScriptEngineSwitcher.ChakraCore.csproj @@ -1,140 +1,68 @@ - - - + + - Debug - AnyCPU - {698A1AFF-B84D-4FB1-B514-D18FFAB5066D} + JS Engine Switcher: ChakraCore + 3.27.3 + net40-client;net45;net471;netstandard1.3;netstandard2.0;netstandard2.1 + 1.6.0 Library - Properties - JavaScriptEngineSwitcher.ChakraCore - JavaScriptEngineSwitcher.ChakraCore - v4.0 - 512 - Client - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true + true + true + $(NoWarn);CS1591;NETSDK1215;NU1903 + false + true + true + + + + + + - ..\..\JavaScriptEngineSwitcher.snk + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png + JavaScriptEngineSwitcher.ChakraCore contains a `ChakraCoreJsEngine` adapter (wrapper for the ChakraCore). + $(PackageCommonTags);ChakraCore + ChakraCore was updated to version of August 1, 2024. + - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Strings.resx - - - True - True - Strings.ru-ru.resx - + + + + - - - {5c903eef-bad1-43b8-bfe2-e4ee4d204410} - JavaScriptEngineSwitcher.Core - + + + - - - JavaScriptEngineSwitcher.snk - - - ResXFileCodeGenerator - Strings.ru-ru.Designer.cs - Designer - + + + - - - ResXFileCodeGenerator - Strings.Designer.cs - + + + + + + + + + - - ChakraCore\x64\ChakraCore.dll - PreserveNewest - - - ChakraCore\x86\ChakraCore.dll - PreserveNewest - + + + - - - - - - - - - - - + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsEngineFactoryCollectionExtensions.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsEngineFactoryCollectionExtensions.cs new file mode 100644 index 00000000..ad6b2b02 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsEngineFactoryCollectionExtensions.cs @@ -0,0 +1,78 @@ +using System; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.ChakraCore +{ + /// + /// JS engine factory collection extensions + /// + public static class JsEngineFactoryCollectionExtensions + { + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Instance of + public static JsEngineFactoryCollection AddChakraCore(this JsEngineFactoryCollection source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return source.AddChakraCore(new ChakraCoreSettings()); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The delegate to configure the provided + /// Instance of + public static JsEngineFactoryCollection AddChakraCore(this JsEngineFactoryCollection source, + Action configure) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var settings = new ChakraCoreSettings(); + configure(settings); + + return source.AddChakraCore(settings); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Settings of the ChakraCore JS engine + /// Instance of + public static JsEngineFactoryCollection AddChakraCore(this JsEngineFactoryCollection source, ChakraCoreSettings settings) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + source.Add(new ChakraCoreJsEngineFactory(settings)); + + return source; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/DefaultExternalBufferFinalizeCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/DefaultExternalBufferFinalizeCallback.cs new file mode 100644 index 00000000..b5cda03a --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/DefaultExternalBufferFinalizeCallback.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ + /// + /// Default callback for finalization of external buffer + /// + internal static class DefaultExternalBufferFinalizeCallback + { + /// + /// Gets a instance of default callback for finalization of external buffer + /// + public static readonly JsFinalizeCallback Instance = Marshal.FreeHGlobal; + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedItem.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedItem.cs new file mode 100644 index 00000000..d9870598 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedItem.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; + +using JavaScriptEngineSwitcher.Core.Utilities; + +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt.Embedding +{ + /// + /// Embedded item + /// + internal abstract class EmbeddedItem : IDisposable + { + /// + /// Host type + /// + private Type _hostType; + + /// + /// Instance of host type + /// + private object _hostObject; + + /// + /// JavaScript value created from an host item + /// + private readonly JsValue _scriptValue; + + /// + /// List of native functions, that used to access to members of host item + /// + private IList _nativeFunctions; + + /// + /// Flag indicating whether this object is disposed + /// + private InterlockedStatedFlag _disposedFlag = new InterlockedStatedFlag(); + + /// + /// Gets a host type + /// + public Type HostType + { + get { return _hostType; } + } + + /// + /// Gets a instance of host type + /// + public object HostObject + { + get { return _hostObject; } + } + + /// + /// Gets a JavaScript value created from an host item + /// + public JsValue ScriptValue + { + get { return _scriptValue; } + } + + /// + /// Gets a list of native functions, that used to access to members of host item + /// + public IList NativeFunctions + { + get { return _nativeFunctions; } + } + + /// + /// Gets a value that indicates if the host item is an instance + /// + public abstract bool IsInstance + { + get; + } + + + /// + /// Constructs an instance of the embedded item + /// + /// Host type + /// Instance of host type + /// JavaScript value created from an host item + /// List of native functions, that used to access to members of host item + protected EmbeddedItem(Type hostType, object hostObject, JsValue scriptValue, + IList nativeFunctions) + { + _hostType = hostType; + _hostObject = hostObject; + _scriptValue = scriptValue; + _nativeFunctions = nativeFunctions; + } + + + #region IDisposable implementation + + /// + /// Disposes the embedded item + /// + public void Dispose() + { + if (_disposedFlag.Set()) + { + _hostType = null; + _hostObject = null; + + IList nativeFunctions = _nativeFunctions; + if (nativeFunctions != null) + { + nativeFunctions.Clear(); + _nativeFunctions = null; + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedObject.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedObject.cs new file mode 100644 index 00000000..612acfec --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedObject.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; + +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt.Embedding +{ + /// + /// Embedded object + /// + internal sealed class EmbeddedObject : EmbeddedItem + { + /// + /// Constructs an instance of the embedded object + /// + /// Instance of host type + /// JavaScript value created from an host object + public EmbeddedObject(object hostObject, JsValue scriptValue) + : base(hostObject.GetType(), hostObject, scriptValue, new List()) + { } + + /// + /// Constructs an instance of the embedded object + /// + /// Instance of host type + /// JavaScript value created from an host object + /// List of native functions, that used to access to members of host object + public EmbeddedObject(object hostObject, JsValue scriptValue, + IList nativeFunctions) + : base(hostObject.GetType(), hostObject, scriptValue, nativeFunctions) + { } + + #region EmbeddedItem overrides + + /// + /// Gets a value that indicates if the host item is an instance + /// + public override bool IsInstance + { + get { return true; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedObjectKey.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedObjectKey.cs new file mode 100644 index 00000000..29033978 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedObjectKey.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +using JavaScriptEngineSwitcher.ChakraCore.Resources; + +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt.Embedding +{ + /// + /// Key for storage of embedded objects + /// + internal struct EmbeddedObjectKey : IEquatable, IStructuralEquatable, + IComparable, IComparable, IStructuralComparable + { + /// + /// Name of host type + /// + public readonly string HostTypeName; + + /// + /// Instance of host type + /// + public readonly object HostObject; + + + /// + /// Constructs an instance of the key for storage of embedded objects + /// + /// Instance of host type + public EmbeddedObjectKey(object hostObject) + { + HostTypeName = hostObject.GetType().AssemblyQualifiedName; + HostObject = hostObject; + } + + + private static int CombineHashCodes(int h1, int h2) + { + return ((h1 << 5) + h1) ^ h2; + } + + #region IEquatable implementation + + public bool Equals(EmbeddedObjectKey other) + { + return EqualityComparer.Default.Equals(HostTypeName, other.HostTypeName) + && EqualityComparer.Default.Equals(HostObject, other.HostObject); + } + + #endregion + + #region IStructuralEquatable implementation + + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + if (other == null || !(other is EmbeddedObjectKey)) + { + return false; + } + + var embeddedObjectKey = (EmbeddedObjectKey)other; + + return comparer.Equals(HostTypeName, embeddedObjectKey.HostTypeName) + && comparer.Equals(HostObject, embeddedObjectKey.HostObject); + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return CombineHashCodes(comparer.GetHashCode(HostTypeName), comparer.GetHashCode(HostObject)); + } + + #endregion + + #region IComparable implementation + + int IComparable.CompareTo(object other) + { + if (other == null) + { + return 1; + } + + if (!(other is EmbeddedObjectKey)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentHasIncorrectType, nameof(other), other.GetType().Name), + nameof(other) + ); + } + + return CompareTo((EmbeddedObjectKey)other); + } + + #endregion + + #region IComparable implementation + + public int CompareTo(EmbeddedObjectKey other) + { + int c = Comparer.Default.Compare(HostTypeName, other.HostTypeName); + if (c != 0) + { + return c; + } + + return Comparer.Default.Compare(HostObject, other.HostObject); + } + + #endregion + + #region IStructuralComparable implementation + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) + { + return 1; + } + + if (!(other is EmbeddedObjectKey)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentHasIncorrectType, nameof(other), other.GetType().Name), + nameof(other) + ); + } + + var embeddedObjectKey = (EmbeddedObjectKey)other; + + int c = comparer.Compare(HostTypeName, embeddedObjectKey.HostTypeName); + if (c != 0) + { + return c; + } + + return comparer.Compare(HostObject, embeddedObjectKey.HostObject); + } + + #endregion + + #region Object overrides + + public override bool Equals(object obj) + { + return obj is EmbeddedObjectKey && Equals((EmbeddedObjectKey)obj); + } + + public override int GetHashCode() + { + return CombineHashCodes(EqualityComparer.Default.GetHashCode(HostTypeName), + EqualityComparer.Default.GetHashCode(HostObject)); + } + + public override string ToString() + { + return "(" + HostTypeName?.ToString() + ", " + HostObject?.ToString() + ")"; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedType.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedType.cs new file mode 100644 index 00000000..edeb7893 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/Embedding/EmbeddedType.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; + +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt.Embedding +{ + /// + /// Embedded type + /// + internal sealed class EmbeddedType : EmbeddedItem + { + /// + /// Constructs an instance of the embedded type + /// + /// Host type + /// JavaScript value created from an host type + public EmbeddedType(Type hostType, JsValue scriptValue) + : base(hostType, null, scriptValue, new List()) + { } + + /// + /// Constructs an instance of the embedded type + /// + /// Host type + /// JavaScript value created from an host type + /// List of native functions, that used to access to members of type + public EmbeddedType(Type hostType, JsValue scriptValue, IList nativeFunctions) + : base(hostType, null, scriptValue, nativeFunctions) + { } + + #region EmbeddedItem overrides + + /// + /// Gets a value that indicates if the host item is an instance + /// + public override bool IsInstance + { + get { return false; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsBackgroundWorkItemCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsBackgroundWorkItemCallback.cs index 4a2d6df9..55c979b3 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsBackgroundWorkItemCallback.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsBackgroundWorkItemCallback.cs @@ -1,7 +1,7 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; +using System; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// The background work item callback /// diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsBeforeCollectCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsBeforeCollectCallback.cs index 998355eb..8f4e7dbe 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsBeforeCollectCallback.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsBeforeCollectCallback.cs @@ -1,7 +1,7 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; +using System; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// The callback called before collection /// diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsContext.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsContext.cs index e1affda8..57df2c43 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsContext.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsContext.cs @@ -1,7 +1,7 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; +using System; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// The script context /// @@ -112,7 +112,7 @@ internal JsContext(IntPtr reference) /// - /// Tells the runtime to do any idle processing it need to do + /// Tells the runtime to do any idle processing it needs to do /// /// /// @@ -142,69 +142,86 @@ public static uint Idle() } /// - /// Parses a script and returns a Function representing the script + /// Parses a script and returns a function representing the script /// /// /// Requires an active script context. /// /// The script to parse - /// The cookie identifying the script that can be used - /// by script contexts that have debugging enabled - /// The location the script came from - /// The Function representing the script code - public static JsValue ParseScript(string script, JsSourceContext sourceContext, string sourceName) + /// A cookie identifying the script that can be used + /// by debuggable script contexts + /// The location the script came from + /// Attribute mask for parsing the script + /// A function representing the script code + public static JsValue ParseScript(string script, JsSourceContext sourceContext, string sourceUrl, + ref JsParseScriptAttributes parseAttributes) { + JsValue scriptValue = JsValue.FromString(script); + scriptValue.AddRef(); + + JsValue sourceUrlValue = JsValue.FromString(sourceUrl); + sourceUrlValue.AddRef(); + JsValue result; - JsErrorHelpers.ThrowIfError(NativeMethods.JsParseScript(script, sourceContext, sourceName, out result)); + + try + { + JsErrorCode errorCode = NativeMethods.JsParse(scriptValue, sourceContext, sourceUrlValue, + parseAttributes, out result); + JsErrorHelpers.ThrowIfError(errorCode); + } + finally + { + scriptValue.Release(); + sourceUrlValue.Release(); + } return result; } /// - /// Parses a serialized script and returns a Function representing the script + /// Parses a serialized script and returns a function representing the script /// /// + /// /// Requires an active script context. + /// + /// + /// The runtime will hold on to the buffer until all instances of any functions created from + /// the buffer are garbage collected. + /// /// /// The script to parse /// The serialized script - /// Thw cookie identifying the script that can be used - /// by script contexts that have debugging enabled - /// The location the script came from - /// The Function representing the script code - public static JsValue ParseScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceName) + /// Callback to load the source code of the serialized script + /// A cookie identifying the script that can be used + /// by debuggable script contexts + /// The location the script came from + /// A function representing the script code + public static JsValue ParseSerializedScript(string script, byte[] buffer, + JsSerializedLoadScriptCallback scriptLoadCallback, JsSourceContext sourceContext, string sourceUrl) { - JsValue result; - JsErrorHelpers.ThrowIfError(NativeMethods.JsParseSerializedScript(script, buffer, sourceContext, sourceName, out result)); + JsValue bufferValue = JsValue.CreateExternalArrayBuffer(buffer); + bufferValue.AddRef(); - return result; - } + JsValue sourceUrlValue = JsValue.FromString(sourceUrl); + sourceUrlValue.AddRef(); - /// - /// Parses a script and returns a Function representing the script - /// - /// - /// Requires an active script context. - /// - /// The script to parse - /// The Function representing the script code - public static JsValue ParseScript(string script) - { - return ParseScript(script, JsSourceContext.None, string.Empty); - } + JsValue result; - /// - /// Parses a serialized script and returns a Function representing the script - /// - /// - /// Requires an active script context. - /// - /// The script to parse - /// The serialized script - /// The Function representing the script code - public static JsValue ParseScript(string script, byte[] buffer) - { - return ParseScript(script, buffer, JsSourceContext.None, string.Empty); + try + { + JsErrorCode errorCode = NativeMethods.JsParseSerialized(bufferValue, scriptLoadCallback, + sourceContext, sourceUrlValue, out result); + JsErrorHelpers.ThrowIfError(errorCode); + } + finally + { + bufferValue.Release(); + sourceUrlValue.Release(); + } + + return result; } /// @@ -214,63 +231,80 @@ public static JsValue ParseScript(string script, byte[] buffer) /// Requires an active script context. /// /// The script to run - /// The cookie identifying the script that can be used - /// by script contexts that have debugging enabled - /// The location the script came from + /// A cookie identifying the script that can be used + /// by debuggable script contexts + /// The location the script came from + /// Attribute mask for parsing the script /// The result of the script, if any - public static JsValue RunScript(string script, JsSourceContext sourceContext, string sourceName) + public static JsValue RunScript(string script, JsSourceContext sourceContext, string sourceUrl, + ref JsParseScriptAttributes parseAttributes) { - JsValue result; - JsErrorHelpers.ThrowIfError(NativeMethods.JsRunScript(script, sourceContext, sourceName, out result)); + JsValue scriptValue = JsValue.FromString(script); + scriptValue.AddRef(); - return result; - } + JsValue sourceUrlValue = JsValue.FromString(sourceUrl); + sourceUrlValue.AddRef(); - /// - /// Runs a serialized script - /// - /// - /// Requires an active script context - /// - /// The source code of the serialized script - /// The serialized script - /// The cookie identifying the script that can be used - /// by script contexts that have debugging enabled - /// The location the script came from - /// The result of the script, if any - public static JsValue RunScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceName) - { JsValue result; - JsErrorHelpers.ThrowIfError(NativeMethods.JsRunSerializedScript(script, buffer, sourceContext, sourceName, out result)); - return result; - } + try + { + JsErrorCode errorCode = NativeMethods.JsRun(scriptValue, sourceContext, sourceUrlValue, + parseAttributes, out result); + JsErrorHelpers.ThrowIfError(errorCode); + } + finally + { + scriptValue.Release(); + sourceUrlValue.Release(); + } - /// - /// Executes a script - /// - /// - /// Requires an active script context - /// - /// The script to run - /// The result of the script, if any - public static JsValue RunScript(string script) - { - return RunScript(script, JsSourceContext.None, string.Empty); + return result; } /// /// Runs a serialized script /// /// + /// /// Requires an active script context. + /// + /// + /// The runtime will detach the data from the buffer and hold on to it until all + /// instances of any functions created from the buffer are garbage collected. + /// /// /// The source code of the serialized script /// The serialized script - /// The result of the script, if any - public static JsValue RunScript(string script, byte[] buffer) + /// Callback to load the source code of the serialized script + /// A cookie identifying the script that can be used + /// by debuggable script contexts + /// The location the script came from + /// The result of running the script, if any + public static JsValue RunSerializedScript(string script, byte[] buffer, + JsSerializedLoadScriptCallback scriptLoadCallback, JsSourceContext sourceContext, string sourceUrl) { - return RunScript(script, buffer, JsSourceContext.None, string.Empty); + JsValue bufferValue = JsValue.CreateExternalArrayBuffer(buffer); + bufferValue.AddRef(); + + JsValue sourceUrlValue = JsValue.FromString(sourceUrl); + sourceUrlValue.AddRef(); + + JsValue result; + + try + { + JsErrorCode errorCode = NativeMethods.JsRunSerialized(bufferValue, scriptLoadCallback, + sourceContext, sourceUrlValue, out result); + JsErrorHelpers.ThrowIfError(errorCode); + } + finally + { + bufferValue.Release(); + sourceUrlValue.Release(); + } + + return result; } /// @@ -278,7 +312,7 @@ public static JsValue RunScript(string script, byte[] buffer) /// /// /// - /// SerializeScript parses a script and then stores the parsed form of the script in a + /// SerializeScript parses a script and then stores the parsed form of the script in a /// runtime-independent format. The serialized script then can be deserialized in any /// runtime without requiring the script to be re-parsed. /// @@ -287,14 +321,28 @@ public static JsValue RunScript(string script, byte[] buffer) /// /// /// The script to serialize - /// The buffer to put the serialized script into. Can be null - /// The size of the buffer, in bytes, required to hold the serialized script - public static ulong SerializeScript(string script, byte[] buffer) + /// Attribute mask for parsing the script + /// The buffer to put the serialized script into + public static byte[] SerializeScript(string script, ref JsParseScriptAttributes parseAttributes) { - var bufferSize = (ulong)buffer.Length; - JsErrorHelpers.ThrowIfError(NativeMethods.JsSerializeScript(script, buffer, ref bufferSize)); + JsValue scriptValue = JsValue.FromString(script); + scriptValue.AddRef(); - return bufferSize; + JsValue bufferValue; + + try + { + JsErrorCode errorCode = NativeMethods.JsSerialize(scriptValue, out bufferValue, parseAttributes); + JsErrorHelpers.ThrowIfError(errorCode); + } + finally + { + scriptValue.Release(); + } + + byte[] buffer = bufferValue.ArrayBufferBytes; + + return buffer; } /// @@ -304,10 +352,10 @@ public static ulong SerializeScript(string script, byte[] buffer) /// /// /// If the runtime of the current context is not in an exception state, this API will throw - /// JsErrorInvalidArgument. If the runtime is disabled, this will return an exception - /// indicating that the script was terminated, but it will not clear the exception (the - /// exception will be cleared if the runtime is re-enabled using - /// EnableRuntimeExecution). + /// . If the runtime is disabled, this will return + /// an exception indicating that the script was terminated, but it will not clear the exception + /// (the exception will be cleared if the runtime is re-enabled using + /// JsEnableRuntimeExecution). /// /// /// Requires an active script context. @@ -316,10 +364,44 @@ public static ulong SerializeScript(string script, byte[] buffer) /// The exception for the runtime of the current context public static JsValue GetAndClearException() { - JsValue reference; - JsErrorHelpers.ThrowIfError(NativeMethods.JsGetAndClearException(out reference)); + JsValue exception; + JsErrorHelpers.ThrowIfError(NativeMethods.JsGetAndClearException(out exception)); + + return exception; + } + + /// + /// Returns a metadata relating to the exception that caused the runtime of the current context + /// to be in the exception state and resets the exception state for that runtime. The metadata + /// includes a reference to the exception itself. + /// + /// + /// + /// If the runtime of the current context is not in an exception state, this API will throw + /// . If the runtime is disabled, this will return + /// an exception indicating that the script was terminated, but it will not clear the exception + /// (the exception will be cleared if the runtime is re-enabled using + /// JsEnableRuntimeExecution). + /// + /// + /// The metadata value is a javascript object with the following properties: exception, the + /// thrown exception object; line, the 0 indexed line number where the exception was thrown; + /// column, the 0 indexed column number where the exception was thrown; length, the + /// source-length of the cause of the exception; source, a string containing the line of + /// source code where the exception was thrown; and url, a string containing the name of + /// the script file containing the code that threw the exception. + /// + /// + /// Requires an active script context. + /// + /// + /// The exception metadata for the runtime of the current context + public static JsValue GetAndClearExceptionWithMetadata() + { + JsValue metadata; + JsErrorHelpers.ThrowIfError(NativeMethods.JsGetAndClearExceptionWithMetadata(out metadata)); - return reference; + return metadata; } /// @@ -340,11 +422,29 @@ public static void SetException(JsValue exception) JsErrorHelpers.ThrowIfError(NativeMethods.JsSetException(exception)); } + /// + /// Sets a promise continuation callback function that is called by the context when a task + /// needs to be queued for future execution + /// + /// + /// + /// Requires an active script context. + /// + /// + /// The callback function being set + /// User provided state that will be passed back to the callback + public static void SetPromiseContinuationCallback(JsPromiseContinuationCallback promiseContinuationCallback, + IntPtr callbackState) + { + JsErrorHelpers.ThrowIfError(NativeMethods.JsSetPromiseContinuationCallback(promiseContinuationCallback, + callbackState)); + } + /// /// Adds a reference to a script context /// /// - /// Calling AddRef ensures that the context will not be freed until Release is called. + /// Calling AddRef ensures that the context will not be freed until Release is called. /// /// The object's new reference count public uint AddRef() @@ -359,7 +459,7 @@ public uint AddRef() /// Releases a reference to a script context /// /// - /// Removes a reference to a context that was created by AddRef. + /// Removes a reference to a context that was created by AddRef. /// /// The object's new reference count public uint Release() diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsEngineException.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsEngineException.cs index 3963c128..5bdbd2bf 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsEngineException.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsEngineException.cs @@ -1,25 +1,45 @@ +#if !NETSTANDARD1_3 +using System; +using System.Runtime.Serialization; + +#endif namespace JavaScriptEngineSwitcher.ChakraCore.JsRt { /// /// The exception that occurred in the workings of the JavaScript engine itself /// - internal sealed class JsEngineException : JsException +#if !NETSTANDARD1_3 + [Serializable] +#endif + public sealed class JsEngineException : JsException { /// /// Initializes a new instance of the class /// - /// The error code returned - public JsEngineException(JsErrorCode code) - : this(code, "A fatal exception has occurred in a JavaScript runtime") + /// The error code returned + public JsEngineException(JsErrorCode errorCode) + : base(errorCode) { } /// /// Initializes a new instance of the class + /// with a specified error message /// - /// The error code returned + /// The error code returned /// The error message - public JsEngineException(JsErrorCode code, string message) - : base(code, message) + public JsEngineException(JsErrorCode errorCode, string message) + : base(errorCode, message) + { } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + private JsEngineException(SerializationInfo info, StreamingContext context) + : base(info, context) { } +#endif } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsErrorCode.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsErrorCode.cs index 88eabbe8..ad9740f4 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsErrorCode.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsErrorCode.cs @@ -3,13 +3,15 @@ /// /// The error code returned from a Chakra hosting API /// - internal enum JsErrorCode : uint + public enum JsErrorCode : uint { /// /// Success error code /// NoError = 0, + #region Usage + /// /// Category of errors that relates to incorrect usage of the API itself /// @@ -21,7 +23,7 @@ internal enum JsErrorCode : uint InvalidArgument, /// - /// An argument to a hosting API was null in a context where null is not allowed + /// An argument to a hosting API was null in a context where null is not allowed /// NullArgument, @@ -123,22 +125,59 @@ internal enum JsErrorCode : uint InObjectBeforeCollectCallback, /// - /// Object cannot be unwrapped to IInspectable pointer + /// Object cannot be unwrapped to IInspectable pointer /// ObjectNotInspectable, /// /// A hosting API that operates on symbol property ids but was called with a non-symbol property id. - /// The error code is returned by JsGetSymbolFromPropertyId if the function is called with non-symbol property id. + /// The error code is returned by JsGetSymbolFromPropertyId if the function is called with non-symbol + /// property id. /// PropertyNotSymbol, /// /// A hosting API that operates on string property ids but was called with a non-string property id. - /// The error code is returned by existing JsGetPropertyNamefromId if the function is called with non-string property id. + /// The error code is returned by existing JsGetPropertyNamefromId if the function is called with + /// non-string property id. /// PropertyNotString, + /// + /// Module evaluation is called in wrong context + /// + InvalidContext, + + /// + /// The Module HostInfoKind provided was invalid + /// + InvalidModuleHostInfoKind, + + /// + /// Module was parsed already when JsParseModuleSource is called + /// + ModuleParsed, + + /// + /// Argument passed to JsCreateWeakReference is a primitive that is not managed by the GC. + /// No weak reference is required, the value will never be collected. + /// + NoWeakRefRequired, + + /// + /// The Promise object is still in the pending state + /// + PromisePending, + + /// + /// Module was not yet evaluated when JsGetModuleNamespace was called + /// + ModuleNotEvaluated, + + #endregion + + #region Engine + /// /// Category of errors that relates to errors occurring within the engine itself /// @@ -149,6 +188,15 @@ internal enum JsErrorCode : uint /// OutOfMemory, + /// + /// The Chakra engine failed to set the Floating Point Unit state + /// + BadFPUState, + + #endregion + + #region Script + /// /// Category of errors that relates to errors in a script /// @@ -170,11 +218,15 @@ internal enum JsErrorCode : uint ScriptTerminated, /// - /// A script was terminated because it tried to use eval or function and eval + /// A script was terminated because it tried to use eval or Function and eval /// was disabled /// ScriptEvalDisabled, + #endregion + + #region Fatal + /// /// Category of errors that are fatal and signify failure of the engine /// @@ -188,6 +240,66 @@ internal enum JsErrorCode : uint /// /// A hosting API was called with object created on different javascript runtime /// - WrongRuntime + WrongRuntime, + + #endregion + + #region Diagnostic + + /// + /// Category of errors that are related to failures during diagnostic operations + /// + CategoryDiagError = 0x50000, + + /// + /// The object for which the debugging API was called was not found + /// + DiagAlreadyInDebugMode, + + /// + /// The debugging API can only be called when VM is in debug mode + /// + DiagNotInDebugMode, + + /// + /// The debugging API can only be called when VM is at a break + /// + DiagNotAtBreak, + + /// + /// Debugging API was called with an invalid handle. + /// + DiagInvalidHandle, + + /// + /// The object for which the debugging API was called was not found + /// + DiagObjectNotFound, + + /// + /// VM was unable to perform the request action + /// + DiagUnableToPerformAction, + + #endregion + + #region Serialization + + /// + /// Serializer/Deserializer does not support current data + /// + SerializerNotSupported, + + /// + /// Current object is not transferable during serialization + /// + TransferableNotSupported, + + /// + /// Current object is already detached when serialized + /// + TransferableAlreadyDetached + + #endregion } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsErrorHelpers.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsErrorHelpers.cs index b56da41a..66ab83fd 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsErrorHelpers.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsErrorHelpers.cs @@ -8,113 +8,162 @@ internal static class JsErrorHelpers /// /// Throws if a native method returns an error code /// - /// The error - public static void ThrowIfError(JsErrorCode error) + /// The error code + public static void ThrowIfError(JsErrorCode errorCode) { - if (error != JsErrorCode.NoError) + if (errorCode != JsErrorCode.NoError) { - switch (error) + switch (errorCode) { + #region Usage + case JsErrorCode.InvalidArgument: - throw new JsUsageException(error, "Invalid argument."); + throw new JsUsageException(errorCode, "Invalid argument."); case JsErrorCode.NullArgument: - throw new JsUsageException(error, "Null argument."); + throw new JsUsageException(errorCode, "Null argument."); case JsErrorCode.NoCurrentContext: - throw new JsUsageException(error, "No current context."); + throw new JsUsageException(errorCode, "No current context."); case JsErrorCode.InExceptionState: - throw new JsUsageException(error, "Runtime is in exception state."); + throw new JsUsageException(errorCode, "Runtime is in exception state."); case JsErrorCode.NotImplemented: - throw new JsUsageException(error, "Method is not implemented."); + throw new JsUsageException(errorCode, "Method is not implemented."); case JsErrorCode.WrongThread: - throw new JsUsageException(error, "Runtime is active on another thread."); + throw new JsUsageException(errorCode, "Runtime is active on another thread."); case JsErrorCode.RuntimeInUse: - throw new JsUsageException(error, "Runtime is in use."); + throw new JsUsageException(errorCode, "Runtime is in use."); case JsErrorCode.BadSerializedScript: - throw new JsUsageException(error, "Bad serialized script."); + throw new JsUsageException(errorCode, "Bad serialized script."); case JsErrorCode.InDisabledState: - throw new JsUsageException(error, "Runtime is disabled."); + throw new JsUsageException(errorCode, "Runtime is disabled."); case JsErrorCode.CannotDisableExecution: - throw new JsUsageException(error, "Cannot disable execution."); - - case JsErrorCode.AlreadyDebuggingContext: - throw new JsUsageException(error, "Context is already in debug mode."); + throw new JsUsageException(errorCode, "Cannot disable execution."); case JsErrorCode.HeapEnumInProgress: - throw new JsUsageException(error, "Heap enumeration is in progress."); + throw new JsUsageException(errorCode, "Heap enumeration is in progress."); case JsErrorCode.ArgumentNotObject: - throw new JsUsageException(error, "Argument is not an object."); + throw new JsUsageException(errorCode, "Argument is not an object."); case JsErrorCode.InProfileCallback: - throw new JsUsageException(error, "In a profile callback."); + throw new JsUsageException(errorCode, "In a profile callback."); case JsErrorCode.InThreadServiceCallback: - throw new JsUsageException(error, "In a thread service callback."); + throw new JsUsageException(errorCode, "In a thread service callback."); case JsErrorCode.CannotSerializeDebugScript: - throw new JsUsageException(error, "Cannot serialize a debug script."); + throw new JsUsageException(errorCode, "Cannot serialize a debug script."); + + case JsErrorCode.AlreadyDebuggingContext: + throw new JsUsageException(errorCode, "Context is already in debug mode."); case JsErrorCode.AlreadyProfilingContext: - throw new JsUsageException(error, "Already profiling this context."); + throw new JsUsageException(errorCode, "Already profiling this context."); case JsErrorCode.IdleNotEnabled: - throw new JsUsageException(error, "Idle is not enabled."); + throw new JsUsageException(errorCode, "Idle is not enabled."); + + case JsErrorCode.CannotSetProjectionEnqueueCallback: + throw new JsUsageException(errorCode, "Cannot set projection enqueue callback."); + + case JsErrorCode.CannotStartProjection: + throw new JsUsageException(errorCode, "Cannot start projection."); + + case JsErrorCode.InObjectBeforeCollectCallback: + throw new JsUsageException(errorCode, "In object before collect callback."); + + case JsErrorCode.ObjectNotInspectable: + throw new JsUsageException(errorCode, "Object not inspectable."); + + case JsErrorCode.PropertyNotSymbol: + throw new JsUsageException(errorCode, "Property not symbol."); + + case JsErrorCode.PropertyNotString: + throw new JsUsageException(errorCode, "Property not string."); + + case JsErrorCode.InvalidContext: + throw new JsUsageException(errorCode, "Invalid context."); + + case JsErrorCode.InvalidModuleHostInfoKind: + throw new JsUsageException(errorCode, "Invalid module host info kind."); + + case JsErrorCode.ModuleParsed: + throw new JsUsageException(errorCode, "Module parsed."); + + case JsErrorCode.NoWeakRefRequired: + throw new JsUsageException(errorCode, "No weak reference is required, the value will never be collected."); + + case JsErrorCode.PromisePending: + throw new JsUsageException(errorCode, "The `Promise` object is still in the pending state."); + + case JsErrorCode.ModuleNotEvaluated: + throw new JsUsageException(errorCode, "Module was not yet evaluated when `JsGetModuleNamespace` was called."); + + #endregion + + #region Engine case JsErrorCode.OutOfMemory: - throw new JsEngineException(error, "Out of memory."); + throw new JsEngineException(errorCode, "Out of memory."); - case JsErrorCode.ScriptException: - { - JsValue errorObject; - JsErrorCode innerError = NativeMethods.JsGetAndClearException(out errorObject); + case JsErrorCode.BadFPUState: + throw new JsEngineException(errorCode, "Bad the Floating Point Unit state."); - if (innerError != JsErrorCode.NoError) - { - throw new JsFatalException(innerError); - } + #endregion - throw new JsScriptException(error, errorObject, "Script threw an exception."); - } + #region Script + case JsErrorCode.ScriptException: case JsErrorCode.ScriptCompile: { - JsValue errorObject; - JsErrorCode innerError = NativeMethods.JsGetAndClearException(out errorObject); + JsValue errorMetadata; + JsErrorCode innerErrorCode = NativeMethods.JsGetAndClearExceptionWithMetadata(out errorMetadata); - if (innerError != JsErrorCode.NoError) + if (innerErrorCode != JsErrorCode.NoError) { - throw new JsFatalException(innerError); + throw new JsFatalException(innerErrorCode); } - throw new JsScriptException(error, errorObject, "Compile error."); + string message = errorCode == JsErrorCode.ScriptCompile ? + "Compile error." : "Script threw an exception."; + + throw new JsScriptException(errorCode, errorMetadata, message); } case JsErrorCode.ScriptTerminated: - throw new JsScriptException(error, JsValue.Invalid, "Script was terminated."); + throw new JsScriptException(errorCode, JsValue.Invalid, "Script was terminated."); case JsErrorCode.ScriptEvalDisabled: - throw new JsScriptException(error, JsValue.Invalid, "Eval of strings is disabled in this runtime."); + throw new JsScriptException(errorCode, JsValue.Invalid, "Eval of strings is disabled in this runtime."); + + #endregion + + #region Fatal case JsErrorCode.Fatal: - throw new JsFatalException(error); + throw new JsFatalException(errorCode, "Fatal error."); + + case JsErrorCode.WrongRuntime: + throw new JsFatalException(errorCode, "Wrong runtime."); + + #endregion default: - throw new JsFatalException(error); + throw new JsFatalException(errorCode); } } } /// - /// Creates a new JavaScript error object + /// Creates a new JavaScript Error object /// /// /// Requires an active script context. @@ -130,7 +179,7 @@ public static JsValue CreateError(string message) } /// - /// Creates a new JavaScript RangeError error object + /// Creates a new JavaScript RangeError error object /// /// /// Requires an active script context. @@ -146,7 +195,7 @@ public static JsValue CreateRangeError(string message) } /// - /// Creates a new JavaScript ReferenceError error object + /// Creates a new JavaScript ReferenceError error object /// /// /// Requires an active script context. @@ -162,7 +211,7 @@ public static JsValue CreateReferenceError(string message) } /// - /// Creates a new JavaScript SyntaxError error object + /// Creates a new JavaScript SyntaxError error object /// /// /// Requires an active script context. @@ -178,7 +227,7 @@ public static JsValue CreateSyntaxError(string message) } /// - /// Creates a new JavaScript TypeError error object + /// Creates a new JavaScript TypeError error object /// /// /// Requires an active script context. @@ -194,7 +243,7 @@ public static JsValue CreateTypeError(string message) } /// - /// Creates a new JavaScript URIError error object + /// Creates a new JavaScript URIError error object /// /// /// Requires an active script context. @@ -208,21 +257,5 @@ public static JsValue CreateUriError(string message) return errorValue; } - - /// - /// Sets a exception - /// - /// - /// Requires an active script context. - /// - /// The error object - public static void SetException(JsValue exception) - { - JsErrorCode innerError = NativeMethods.JsSetException(exception); - if (innerError != JsErrorCode.NoError) - { - throw new JsFatalException(innerError); - } - } } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsException.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsException.cs index 0d457a7f..3577c8bf 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsException.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsException.cs @@ -1,57 +1,89 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +using System.Security.Permissions; +#endif + namespace JavaScriptEngineSwitcher.ChakraCore.JsRt { - using System; - /// /// The exception returned from the Chakra engine /// - internal class JsException : Exception +#if !NETSTANDARD1_3 + [Serializable] +#endif + public class JsException : Exception { /// /// The error code /// - private readonly JsErrorCode _code; + private readonly JsErrorCode _errorCode; /// /// Gets a error code /// public JsErrorCode ErrorCode { - get { return _code; } + get { return _errorCode; } } /// /// Initializes a new instance of the class /// - /// The error code returned - public JsException(JsErrorCode code) - : this(code, "A fatal exception has occurred in a JavaScript runtime") + /// The error code returned + public JsException(JsErrorCode errorCode) + : this(errorCode, "A fatal exception has occurred in a JavaScript runtime") { } /// /// Initializes a new instance of the class + /// with a specified error message /// - /// The error code returned + /// The error code returned /// The error message - public JsException(JsErrorCode code, string message) + public JsException(JsErrorCode errorCode, string message) : base(message) { - _code = code; + _errorCode = errorCode; } +#if !NETSTANDARD1_3 /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class with serialized data /// - /// The error message - /// The exception that is the cause of the current exception - protected JsException(string message, Exception innerException) - : base(message, innerException) + /// The object that holds the serialized data + /// The contextual information about the source or destination + protected JsException(SerializationInfo info, StreamingContext context) + : base(info, context) { - if (message != null) + if (info != null) { - _code = (JsErrorCode) HResult; + _errorCode = (JsErrorCode)info.GetUInt32("ErrorCode"); } } + + + #region Exception overrides + + /// + /// Populates a with the data needed to serialize the target object + /// + /// The to populate with data + /// The destination (see ) for this serialization + [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + base.GetObjectData(info, context); + info.AddValue("ErrorCode", (uint)_errorCode); + } + + #endregion +#endif } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsFatalException.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsFatalException.cs index 6fb49e5b..d9e42100 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsFatalException.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsFatalException.cs @@ -1,25 +1,45 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +#if !NETSTANDARD1_3 +using System; +using System.Runtime.Serialization; + +#endif +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt { /// /// The fatal exception occurred /// - internal sealed class JsFatalException : JsException +#if !NETSTANDARD1_3 + [Serializable] +#endif + public sealed class JsFatalException : JsException { /// /// Initializes a new instance of the class /// - /// The error code returned - public JsFatalException(JsErrorCode code) - : this(code, "A fatal exception has occurred in a JavaScript runtime") + /// The error code returned + public JsFatalException(JsErrorCode errorCode) + : base(errorCode) { } /// /// Initializes a new instance of the class + /// with a specified error message /// - /// The error code returned + /// The error code returned /// The error message - public JsFatalException(JsErrorCode code, string message) - : base(code, message) + public JsFatalException(JsErrorCode errorCode, string message) + : base(errorCode, message) + { } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + private JsFatalException(SerializationInfo info, StreamingContext context) + : base(info, context) { } +#endif } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsFinalizeCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsFinalizeCallback.cs new file mode 100644 index 00000000..be7f4cb3 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsFinalizeCallback.cs @@ -0,0 +1,10 @@ +using System; + +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ + /// + /// A finalizer callback + /// + /// The external data that was passed in when creating the object being finalized + internal delegate void JsFinalizeCallback(IntPtr data); +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsMemoryAllocationCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsMemoryAllocationCallback.cs index 1f83743e..c16505a3 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsMemoryAllocationCallback.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsMemoryAllocationCallback.cs @@ -1,15 +1,15 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; +using System; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// User implemented callback routine for memory allocation events /// - /// The state passed to SetRuntimeMemoryAllocationCallback + /// The state passed to SetRuntimeMemoryAllocationCallback /// The type of type allocation event /// The size of the allocation - /// For the Allocate event, returning true allows the runtime to continue with - /// allocation. Returning false indicates the allocation request is rejected. The return value + /// For the Allocate event, returning true allows the runtime to continue with + /// allocation. Returning false indicates the allocation request is rejected. The return value /// is ignored for other allocation events. internal delegate bool JsMemoryAllocationCallback(IntPtr callbackState, JsMemoryEventType allocationEvent, UIntPtr allocationSize); } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsNativeFunction.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsNativeFunction.cs index d748dce8..5ef45d2d 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsNativeFunction.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsNativeFunction.cs @@ -1,13 +1,13 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; - using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// The function callback /// /// The Function object that represents the function being invoked - /// Indicates whether this is a regular call or a 'new' call + /// Indicates whether this is a regular call or a new call /// The arguments to the call /// The number of arguments /// Callback data, if any diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsObjectBeforeCollectCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsObjectBeforeCollectCallback.cs index c7cb5b1d..062b41ce 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsObjectBeforeCollectCallback.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsObjectBeforeCollectCallback.cs @@ -1,7 +1,7 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; +using System; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// The callback called before collecting an object /// diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsObjectFinalizeCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsObjectFinalizeCallback.cs deleted file mode 100644 index 039cf68d..00000000 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsObjectFinalizeCallback.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; - - /// - /// The finalization callback - /// - /// The external data that was passed in when creating the object being finalized - internal delegate void JsObjectFinalizeCallback(IntPtr data); -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsParseScriptAttributes.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsParseScriptAttributes.cs new file mode 100644 index 00000000..426c042f --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsParseScriptAttributes.cs @@ -0,0 +1,33 @@ +using System; + +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ + /// + /// Attribute mask for JsParseScriptWithAttributes + /// + [Flags] + internal enum JsParseScriptAttributes + { + /// + /// Default attribute + /// + None = 0x0, + + /// + /// Specified script is internal and non-user code. + /// Hidden from debugger. + /// + LibraryCode = 0x1, + + /// + /// ChakraCore assumes ExternalArrayBuffer is Utf8 by default. + /// This one needs to be set for Utf16. + /// + ArrayBufferIsUtf16Encoded = 0x2, + + /// + /// Script should be parsed in strict mode + /// + StrictMode = 0x4 + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPromiseContinuationCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPromiseContinuationCallback.cs index fb74d621..010f3dcd 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPromiseContinuationCallback.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPromiseContinuationCallback.cs @@ -1,7 +1,7 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; +using System; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// The promise continuation callback /// diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPropertyId.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPropertyId.cs index 09e611a2..9382d237 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPropertyId.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPropertyId.cs @@ -1,7 +1,19 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; +using System; +#if NET45_OR_GREATER || NETSTANDARD +using System.Buffers; +using System.Runtime.InteropServices; +#endif +using System.Text; +#if NET40 + +using PolyfillsForOldDotNet.System.Buffers; +using PolyfillsForOldDotNet.System.Runtime.InteropServices; +#endif + +using JavaScriptEngineSwitcher.ChakraCore.Helpers; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// The property identifier /// @@ -28,16 +40,38 @@ public static JsPropertyId Invalid /// Gets a name associated with the property ID /// /// - /// /// Requires an active script context. - /// /// public string Name { get { + byte[] buffer = null; + UIntPtr bufferSize = UIntPtr.Zero; + UIntPtr length; + + JsErrorCode errorCode = NativeMethods.JsCopyPropertyId(this, buffer, bufferSize, out length); + JsErrorHelpers.ThrowIfError(errorCode); + + var byteArrayPool = ArrayPool.Shared; + bufferSize = length; + int bufferLength = (int)bufferSize; + buffer = byteArrayPool.Rent(bufferLength + 1); + buffer[bufferLength] = 0; + string name; - JsErrorHelpers.ThrowIfError(NativeMethods.JsGetPropertyNameFromId(this, out name)); + + try + { + errorCode = NativeMethods.JsCopyPropertyId(this, buffer, bufferSize, out length); + JsErrorHelpers.ThrowIfError(errorCode); + + name = Encoding.UTF8.GetString(buffer, 0, bufferLength); + } + finally + { + byteArrayPool.Return(buffer); + } return name; } @@ -66,12 +100,29 @@ internal JsPropertyId(IntPtr id) /// /// /// The name of the property ID to get or create. - /// The name may consist of only digits. + /// The string is expected to be ASCII / utf8 encoded. + /// The name can be any JavaScript property identifier, including all digits. /// The property ID in this runtime for the given name public static JsPropertyId FromString(string name) { + string processedName; + int byteCount; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + processedName = EncodingHelpers.UnicodeToAnsi(name, out byteCount); + } + else + { + processedName = name; + byteCount = Encoding.UTF8.GetByteCount(processedName); + } + JsPropertyId id; - JsErrorHelpers.ThrowIfError(NativeMethods.JsGetPropertyIdFromName(name, out id)); + UIntPtr length = new UIntPtr((uint)byteCount); + + JsErrorCode errorCode = NativeMethods.JsCreatePropertyId(processedName, length, out id); + JsErrorHelpers.ThrowIfError(errorCode); return id; } diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPropertyIdType.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPropertyIdType.cs index cf6238c0..7899f17b 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPropertyIdType.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsPropertyIdType.cs @@ -8,11 +8,11 @@ internal enum JsPropertyIdType /// /// String property /// - JavaScriptPropertyIdTypeString, + String, /// /// Symbol property /// - JavaScriptPropertyIdTypeSymbol + Symbol } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsRuntime.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsRuntime.cs index 12220e84..367d4f9e 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsRuntime.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsRuntime.cs @@ -1,7 +1,7 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; +using System; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// The Chakra runtime /// @@ -15,9 +15,9 @@ /// time. /// /// - /// NOTE: A JavaScriptRuntime, unlike other objects in the Chakra hosting API, is not + /// NOTE: A JsRuntime, unlike other objects in the Chakra hosting API, is not /// garbage collected since it contains the garbage collected heap itself. A runtime will - /// continue to exist until Dispose is called. + /// continue to exist until Dispose is called. /// /// internal struct JsRuntime : IDisposable @@ -101,36 +101,34 @@ public bool Disabled /// /// Creates a new runtime /// - /// The attributes of the runtime to be created - /// The version of the runtime to be created - /// The thread service for the runtime. Can be null /// The runtime created - public static JsRuntime Create(JsRuntimeAttributes attributes, JsRuntimeVersion version, JsThreadServiceCallback threadServiceCallback) + public static JsRuntime Create() { - JsRuntime handle; - JsErrorHelpers.ThrowIfError(NativeMethods.JsCreateRuntime(attributes, threadServiceCallback, out handle)); - - return handle; + return Create(JsRuntimeAttributes.None, null); } /// /// Creates a new runtime /// /// The attributes of the runtime to be created - /// The version of the runtime to be created /// The runtime created - public static JsRuntime Create(JsRuntimeAttributes attributes, JsRuntimeVersion version) + public static JsRuntime Create(JsRuntimeAttributes attributes) { - return Create(attributes, version, null); + return Create(attributes, null); } /// /// Creates a new runtime /// + /// The attributes of the runtime to be created + /// The thread service for the runtime. Can be null /// The runtime created - public static JsRuntime Create() + public static JsRuntime Create(JsRuntimeAttributes attributes, JsThreadServiceCallback threadServiceCallback) { - return Create(JsRuntimeAttributes.None, JsRuntimeVersion.Version11, null); + JsRuntime handle; + JsErrorHelpers.ThrowIfError(NativeMethods.JsCreateRuntime(attributes, threadServiceCallback, out handle)); + + return handle; } /// @@ -149,7 +147,7 @@ public void CollectGarbage() /// Registering a memory allocation callback will cause the runtime to call back to the host /// whenever it acquires memory from, or releases memory to, the OS. The callback routine is /// called before the runtime memory manager allocates a block of memory. The allocation will - /// be rejected if the callback returns false. The runtime memory manager will also invoke the + /// be rejected if the callback returns false. The runtime memory manager will also invoke the /// callback routine after freeing a block of memory, as well as after allocation failures. /// /// diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsRuntimeAttributes.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsRuntimeAttributes.cs index 167f27a3..92d616e4 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsRuntimeAttributes.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsRuntimeAttributes.cs @@ -1,8 +1,8 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; - using System.Diagnostics.CodeAnalysis; +using System; +using System.Diagnostics.CodeAnalysis; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// Attributes of a runtime /// @@ -27,7 +27,7 @@ internal enum JsRuntimeAttributes AllowScriptInterrupt = 0x00000002, /// - /// Host will call Idle, so enable idle processing. Otherwise, the runtime will manage + /// Host will call Idle, so enable idle processing. Otherwise, the runtime will manage /// memory slightly more aggressively. /// EnableIdleProcessing = 0x00000004, @@ -38,7 +38,7 @@ internal enum JsRuntimeAttributes DisableNativeCodeGeneration = 0x00000008, /// - /// Using Eval or Function constructor will throw an exception + /// Using eval or Function constructor will throw an exception /// [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Eval is a valid function name.")] DisableEval = 0x00000010, @@ -52,6 +52,20 @@ internal enum JsRuntimeAttributes /// Calling JsSetException will also dispatch the exception to the script debugger /// (if any) giving the debugger a chance to break on the exception /// - DispatchSetExceptionsToDebugger = 0x00000040 + DispatchSetExceptionsToDebugger = 0x00000040, + + /// + /// Disable Failfast fatal error on OOM + /// + DisableFatalOnOOM = 0x00000080, + + /// + /// Runtime will not allocate executable code pages. + /// This also implies that Native Code generation will be turned off. + /// Note that this will break JavaScript stack decoding in tools like WPA since they + /// rely on allocation of unique thunks to interpret each function and allocation of + /// those thunks will be disabled as well. + /// + DisableExecutablePageAllocation = 0x00000100 } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsRuntimeVersion.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsRuntimeVersion.cs deleted file mode 100644 index 79bea43e..00000000 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsRuntimeVersion.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - /// - /// Version of the runtime - /// - internal enum JsRuntimeVersion - { - /// - /// Create runtime with IE10 version - /// - Version10 = 0, - - /// - /// Create runtime with IE11 version - /// - Version11 = 1, - - /// - /// Create runtime with highest version present on the machine at runtime - /// - VersionEdge = -1 - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsScope.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsScope.cs index c0a8bba1..06e7e079 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsScope.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsScope.cs @@ -1,9 +1,7 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; - - using Core; +using System; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// The scope automatically sets a context to current and resets the original context /// when disposed @@ -18,7 +16,7 @@ internal struct JsScope : IDisposable /// /// Whether the structure has been disposed /// - private StatedFlag _disposedFlag; + private bool _disposed; /// @@ -27,7 +25,7 @@ internal struct JsScope : IDisposable /// The context to create the scope for public JsScope(JsContext context) { - _disposedFlag = new StatedFlag(); + _disposed = false; _previousContext = JsContext.Current; JsContext.Current = context; @@ -41,10 +39,13 @@ public JsScope(JsContext context) /// public void Dispose() { - if (_disposedFlag.Set()) + if (_disposed) { - JsContext.Current = _previousContext; + return; } + + JsContext.Current = _previousContext; + _disposed = true; } #endregion diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsScriptException.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsScriptException.cs index a9b6b025..0d96043f 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsScriptException.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsScriptException.cs @@ -1,57 +1,74 @@ +#if !NETSTANDARD1_3 +using System; +using System.Runtime.Serialization; + +#endif namespace JavaScriptEngineSwitcher.ChakraCore.JsRt { - using System; - /// /// The script exception /// - internal sealed class JsScriptException : JsException +#if !NETSTANDARD1_3 + [Serializable] +#endif + public sealed class JsScriptException : JsException { /// - /// The error + /// The error metadata /// - private readonly JsValue _error; +#if !NETSTANDARD1_3 + [NonSerialized] +#endif + private readonly JsValue _metadata; /// - /// Gets a JavaScript object representing the script error + /// Gets a JavaScript object representing the error metadata /// - public JsValue Error + internal JsValue Metadata { - get - { - return _error; - } + get { return _metadata; } } /// /// Initializes a new instance of the class /// - /// The error code returned - /// The JavaScript error object - public JsScriptException(JsErrorCode code, JsValue error) - : this(code, error, "JavaScript Exception") + /// The error code returned + public JsScriptException(JsErrorCode errorCode) + : this(errorCode, "JavaScript Exception") { } /// /// Initializes a new instance of the class /// - /// The error code returned - /// The JavaScript error object + /// The error code returned /// The error message - public JsScriptException(JsErrorCode code, JsValue error, string message) - : base(code, message) - { - _error = error; - } + public JsScriptException(JsErrorCode errorCode, string message) + : this(errorCode, JsValue.Invalid, message) + { } /// /// Initializes a new instance of the class + /// with a specified error message /// + /// The error code returned + /// The JavaScript error metadata /// The error message - /// The exception that is the cause of the current exception - private JsScriptException(string message, Exception innerException) - : base(message, innerException) + internal JsScriptException(JsErrorCode errorCode, JsValue metadata, string message) + : base(errorCode, message) + { + _metadata = metadata; + } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + private JsScriptException(SerializationInfo info, StreamingContext context) + : base(info, context) { } +#endif } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSerializedLoadScriptCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSerializedLoadScriptCallback.cs new file mode 100644 index 00000000..62ba351a --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSerializedLoadScriptCallback.cs @@ -0,0 +1,13 @@ +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ + /// + /// Called by the runtime to load the source code of the serialized script + /// + /// A cookie identifying the script that can be used + /// by debuggable script contexts + /// The script returned + /// Attribute mask for parsing the script + /// true if the operation succeeded, false otherwise + internal delegate bool JsSerializedLoadScriptCallback(JsSourceContext sourceContext, + out JsValue value, out JsParseScriptAttributes parseAttributes); +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSerializedScriptLoadSourceCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSerializedScriptLoadSourceCallback.cs index 0e9718a1..90975001 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSerializedScriptLoadSourceCallback.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSerializedScriptLoadSourceCallback.cs @@ -2,10 +2,10 @@ { /// /// Called by the runtime to load the source code of the serialized script. - /// The caller must keep the script buffer valid until the JsSerializedScriptUnloadCallback. + /// The caller must keep the script buffer valid until the JsSerializedScriptUnloadCallback. /// - /// The context passed to Js[Parse|Run]SerializedScriptWithCallback + /// The context passed to Js[Parse|Run]SerializedScriptWithCallback /// The script returned - /// true if the operation succeeded, false otherwise + /// true if the operation succeeded, false otherwise internal delegate bool JsSerializedScriptLoadSourceCallback(JsSourceContext sourceContext, out string scriptBuffer); } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSerializedScriptUnloadCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSerializedScriptUnloadCallback.cs index 0dbb10f9..f474f3ec 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSerializedScriptUnloadCallback.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSerializedScriptUnloadCallback.cs @@ -4,6 +4,6 @@ /// Called by the runtime when it is finished with all resources related to the script execution. /// The caller should free the source if loaded, the byte code, and the context at this time. /// - /// The context passed to Js[Parse|Run]SerializedScriptWithCallback + /// The context passed to Js[Parse|Run]SerializedScriptWithCallback internal delegate void JsSerializedScriptUnloadCallback(JsSourceContext sourceContext); } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSourceContext.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSourceContext.cs index 5326fa96..146efdd7 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSourceContext.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsSourceContext.cs @@ -1,7 +1,7 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; +using System; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// The cookie that identifies a script for debugging purposes /// diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsThreadServiceCallback.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsThreadServiceCallback.cs index 298b1347..7d5e67ad 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsThreadServiceCallback.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsThreadServiceCallback.cs @@ -1,7 +1,7 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; +using System; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// The thread service callback /// @@ -9,7 +9,7 @@ /// The host can specify a background thread service when creating a runtime. If /// specified, then background work items will be passed to the host using this callback. The /// host is expected to either begin executing the background work item immediately and return - /// true or return false and the runtime will handle the work item in-thread. + /// true or return false and the runtime will handle the work item in-thread. /// /// The callback for the background work item /// The data argument to be passed to the callback diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsTypedArrayType.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsTypedArrayType.cs index 8ce1b790..e36c6a1d 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsTypedArrayType.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsTypedArrayType.cs @@ -33,7 +33,7 @@ internal enum JsTypedArrayType /// /// An int32 array /// - TypeInt32, + Int32, /// /// An uint32 array diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsUsageException.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsUsageException.cs index 7ae4527e..cbaf2d48 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsUsageException.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsUsageException.cs @@ -1,25 +1,45 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +#if !NETSTANDARD1_3 +using System; +using System.Runtime.Serialization; + +#endif +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt { /// /// The API usage exception occurred /// - internal sealed class JsUsageException : JsException +#if !NETSTANDARD1_3 + [Serializable] +#endif + public sealed class JsUsageException : JsException { /// /// Initializes a new instance of the class /// - /// The error code returned - public JsUsageException(JsErrorCode code) - : this(code, "A fatal exception has occurred in a JavaScript runtime") + /// The error code returned + public JsUsageException(JsErrorCode errorCode) + : base(errorCode) { } /// /// Initializes a new instance of the class + /// with a specified error message /// - /// The error code returned + /// The error code returned /// The error message - public JsUsageException(JsErrorCode code, string message) - : base(code, message) + public JsUsageException(JsErrorCode errorCode, string message) + : base(errorCode, message) + { } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + private JsUsageException(SerializationInfo info, StreamingContext context) + : base(info, context) { } +#endif } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValue.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValue.cs index 0450a2e4..dded5494 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValue.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValue.cs @@ -1,14 +1,23 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +using System; +#if NET45_OR_GREATER || NETSTANDARD +using System.Buffers; +#endif +using System.Runtime.InteropServices; +using System.Text; +#if NET40 + +using PolyfillsForOldDotNet.System.Buffers; +using PolyfillsForOldDotNet.System.Runtime.InteropServices; +#endif + +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt { - using System; - using System.Runtime.InteropServices; - /// /// The JavaScript value /// /// - /// The JavaScript value is one of the following types of values: Undefined, Null, Boolean, - /// String, Number, or Object. + /// The JavaScript value is one of the following types of values: undefined, null, Boolean, + /// String, Number, or Object. /// internal struct JsValue { @@ -231,6 +240,39 @@ public IntPtr ExternalData } } + /// + /// Gets a bytes from an ArrayBuffer + /// + /// + /// Requires an active script context. + /// + public byte[] ArrayBufferBytes + { + get + { + IntPtr bufferPtr; + uint bufferLength; + + JsErrorCode errorCode = NativeMethods.JsGetArrayBufferStorage(this, out bufferPtr, out bufferLength); + JsErrorHelpers.ThrowIfError(errorCode); + + if (bufferPtr == IntPtr.Zero) + { + return null; + } + + if (bufferLength == 0) + { + return new byte[0]; + } + + var buffer = new byte[bufferLength]; + Marshal.Copy(bufferPtr, buffer, 0, (int)bufferLength); + + return buffer; + } + } + /// /// Initializes a new instance of the struct @@ -296,12 +338,25 @@ public static JsValue FromInt32(int value) /// /// Requires an active script context. /// - /// The string to convert to a String value + /// The string to convert to a String value /// The new String value public static JsValue FromString(string value) { JsValue reference; - JsErrorHelpers.ThrowIfError(NativeMethods.JsPointerToString(value, new UIntPtr((uint)value.Length), out reference)); + JsErrorCode errorCode; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var stringLength = new UIntPtr((uint)value.Length); + errorCode = NativeMethods.JsCreateStringUtf16(value, stringLength, out reference); + } + else + { + var byteCount = new UIntPtr((uint)Encoding.UTF8.GetByteCount(value)); + errorCode = NativeMethods.JsCreateString(value, byteCount, out reference); + } + + JsErrorHelpers.ThrowIfError(errorCode); return reference; } @@ -327,10 +382,10 @@ public static JsValue CreateObject() /// /// Requires an active script context. /// - /// External data that the object will represent. May be null - /// The callback for when the object is finalized. May be null. + /// External data that the object will represent. May be null. + /// The callback for when the object is finalized. May be null. /// The new Object - public static JsValue CreateExternalObject(IntPtr data, JsObjectFinalizeCallback finalizer) + public static JsValue CreateExternalObject(IntPtr data, JsFinalizeCallback finalizer) { JsValue reference; JsErrorHelpers.ThrowIfError(NativeMethods.JsCreateExternalObject(data, finalizer, out reference)); @@ -388,7 +443,34 @@ public static JsValue CreateArray(uint length) } /// - /// Creates a new JavaScript error object + /// Creates a Javascript ArrayBuffer object to access external memory + /// + /// Requires an active script context. + /// Buffer for an external memory + /// The new ArrayBuffer object + public static JsValue CreateExternalArrayBuffer(byte[] buffer) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + int bufferLength = buffer.Length; + IntPtr bufferPtr = Marshal.AllocHGlobal(bufferLength + 1); + + Marshal.Copy(buffer, 0, bufferPtr, bufferLength); + Marshal.WriteByte(bufferPtr, bufferLength, 0); + + JsValue reference; + JsErrorCode errorCode = NativeMethods.JsCreateExternalArrayBuffer(bufferPtr, (uint)bufferLength, + DefaultExternalBufferFinalizeCallback.Instance, bufferPtr, out reference); + JsErrorHelpers.ThrowIfError(errorCode); + + return reference; + } + + /// + /// Creates a new JavaScript Error object /// /// /// Requires an active script context. @@ -404,7 +486,7 @@ public static JsValue CreateError(JsValue message) } /// - /// Creates a new JavaScript RangeError error object + /// Creates a new JavaScript RangeError error object /// /// /// Requires an active script context. @@ -420,7 +502,7 @@ public static JsValue CreateRangeError(JsValue message) } /// - /// Creates a new JavaScript ReferenceError error object + /// Creates a new JavaScript ReferenceError error object /// /// /// Requires an active script context. @@ -436,7 +518,7 @@ public static JsValue CreateReferenceError(JsValue message) } /// - /// Creates a new JavaScript SyntaxError error object + /// Creates a new JavaScript SyntaxError error object /// /// /// Requires an active script context. @@ -452,7 +534,7 @@ public static JsValue CreateSyntaxError(JsValue message) } /// - /// Creates a new JavaScript TypeError error object + /// Creates a new JavaScript TypeError error object /// /// /// Requires an active script context. @@ -468,7 +550,7 @@ public static JsValue CreateTypeError(JsValue message) } /// - /// Creates a new JavaScript URIError error object + /// Creates a new JavaScript URIError error object /// /// /// Requires an active script context. @@ -488,8 +570,8 @@ public static JsValue CreateUriError(JsValue message) /// /// /// This only needs to be called on objects that are not going to be stored somewhere on - /// the stack. Calling AddRef ensures that the JavaScript object the value refers to will not be freed - /// until Release is called + /// the stack. Calling AddRef ensures that the JavaScript object the value refers to will not be freed + /// until Release is called /// /// The object's new reference count public uint AddRef() @@ -504,7 +586,7 @@ public uint AddRef() /// Releases a reference to the object /// /// - /// Removes a reference that was created by AddRef. + /// Removes a reference that was created by AddRef. /// /// The object's new reference count public uint Release() @@ -535,7 +617,7 @@ public bool ToBoolean() /// /// /// - /// This function retrieves the value of a Number value. It will fail with + /// This function retrieves the value of a Number value. It will fail with /// InvalidArgument if the type of the value is not Number. /// /// @@ -551,6 +633,27 @@ public double ToDouble() return value; } + /// + /// Retrieves a int value of a Number value + /// + /// + /// + /// This function retrieves the value of a Number value. It will fail with + /// InvalidArgument if the type of the value is not Number. + /// + /// + /// Requires an active script context. + /// + /// + /// The int value + public int ToInt32() + { + int value; + JsErrorHelpers.ThrowIfError(NativeMethods.JsNumberToInt(this, out value)); + + return value; + } + /// /// Retrieves a string pointer of a String value /// @@ -566,12 +669,65 @@ public double ToDouble() /// The string public new string ToString() { - IntPtr buffer; - UIntPtr length; + string result; + JsErrorCode errorCode; - JsErrorHelpers.ThrowIfError(NativeMethods.JsStringToPointer(this, out buffer, out length)); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + int start = 0; + int length = int.MaxValue; + char[] buffer = null; + UIntPtr written; + + errorCode = NativeMethods.JsCopyStringUtf16(this, start, length, buffer, out written); + JsErrorHelpers.ThrowIfError(errorCode); + + var charArrayPool = ArrayPool.Shared; + length = (int)written; + buffer = charArrayPool.Rent(length + 1); + buffer[length] = '\0'; + + try + { + errorCode = NativeMethods.JsCopyStringUtf16(this, start, length, buffer, out written); + JsErrorHelpers.ThrowIfError(errorCode); + + result = new string(buffer, start, length); + } + finally + { + charArrayPool.Return(buffer); + } + } + else + { + byte[] buffer = null; + UIntPtr bufferSize = UIntPtr.Zero; + UIntPtr length; + + errorCode = NativeMethods.JsCopyString(this, buffer, bufferSize, out length); + JsErrorHelpers.ThrowIfError(errorCode); + + var byteArrayPool = ArrayPool.Shared; + bufferSize = length; + int bufferLength = (int)bufferSize; + buffer = byteArrayPool.Rent(bufferLength + 1); + buffer[bufferLength] = 0; + + try + { + errorCode = NativeMethods.JsCopyString(this, buffer, bufferSize, out length); + JsErrorHelpers.ThrowIfError(errorCode); + + result = Encoding.UTF8.GetString(buffer, 0, bufferLength); + } + finally + { + byteArrayPool.Return(buffer); + } + } - return Marshal.PtrToStringUni(buffer, (int)length); + return result; } /// @@ -692,6 +848,22 @@ public bool HasProperty(JsPropertyId propertyId) return hasProperty; } + /// + /// Determines whether an object has a non-inherited property + /// + /// + /// Requires an active script context. + /// + /// The ID of the property + /// Whether the object has the non-inherited property + public bool HasOwnProperty(JsPropertyId propertyId) + { + bool hasOwnProperty; + JsErrorHelpers.ThrowIfError(NativeMethods.JsHasOwnProperty(this, propertyId, out hasOwnProperty)); + + return hasOwnProperty; + } + /// /// Gets an object's property /// @@ -818,7 +990,7 @@ public void DeleteIndexedProperty(JsValue index) /// /// /// - /// This function is equivalent to the "==" operator in JavaScript. + /// This function is equivalent to the == operator in JavaScript. /// /// /// Requires an active script context. @@ -839,7 +1011,7 @@ public bool Equals(JsValue other) /// /// /// - /// This function is equivalent to the "===" operator in JavaScript. + /// This function is equivalent to the === operator in JavaScript. /// /// /// Requires an active script context. @@ -862,14 +1034,14 @@ public bool StrictEquals(JsValue other) /// Requires an active script context. /// /// The arguments to the call - /// The Value returned from the function invocation, if any + /// The JavaScript value returned from the function invocation, if any public JsValue CallFunction(params JsValue[] arguments) { JsValue returnReference; if (arguments.Length > ushort.MaxValue) { - throw new ArgumentOutOfRangeException("arguments"); + throw new ArgumentOutOfRangeException(nameof(arguments)); } JsErrorHelpers.ThrowIfError(NativeMethods.JsCallFunction(this, arguments, (ushort)arguments.Length, out returnReference)); @@ -884,14 +1056,14 @@ public JsValue CallFunction(params JsValue[] arguments) /// Requires an active script context. /// /// The arguments to the call - /// The Value returned from the function invocation + /// The JavaScript value returned from the function invocation public JsValue ConstructObject(params JsValue[] arguments) { JsValue returnReference; if (arguments.Length > ushort.MaxValue) { - throw new ArgumentOutOfRangeException("arguments"); + throw new ArgumentOutOfRangeException(nameof(arguments)); } JsErrorHelpers.ThrowIfError(NativeMethods.JsConstructObject(this, arguments, (ushort)arguments.Length, out returnReference)); diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValueExtensions.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValueExtensions.cs index 1fbd8e18..ee2aa7c0 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValueExtensions.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValueExtensions.cs @@ -39,6 +39,23 @@ public static bool HasProperty(this JsValue source, string propertyName) return result; } + /// + /// Determines whether an object has a non-inherited property + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// Whether the object has the non-inherited property + public static bool HasOwnProperty(this JsValue source, string propertyName) + { + JsPropertyId propertyId = JsPropertyId.FromString(propertyName); + bool result = source.HasOwnProperty(propertyId); + + return result; + } + /// /// Gets an object's property /// diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValueType.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValueType.cs index 913016e7..f541875c 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValueType.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/JsValueType.cs @@ -1,7 +1,7 @@ namespace JavaScriptEngineSwitcher.ChakraCore.JsRt { /// - /// The JavaScript type of a JavaScriptValue + /// The JavaScript type of a JavaScript value /// internal enum JsValueType { @@ -16,47 +16,47 @@ internal enum JsValueType Null = 1, /// - /// The value is a JavaScript number value + /// The value is a JavaScript Number value /// Number = 2, /// - /// The value is a JavaScript string value + /// The value is a JavaScript String value /// String = 3, /// - /// The value is a JavaScript Boolean value + /// The value is a JavaScript Boolean value /// Boolean = 4, /// - /// The value is a JavaScript object value + /// The value is a JavaScript Object value /// Object = 5, /// - /// The value is a JavaScript function object value + /// The value is a JavaScript Function object value /// Function = 6, /// - /// The value is a JavaScript error object value + /// The value is a JavaScript Error object value /// Error = 7, /// - /// The value is a JavaScript array object value + /// The value is a JavaScript Array object value /// Array = 8, /// - /// The value is a JavaScript array object value + /// The value is a JavaScript Symbol object value /// Symbol = 9, /// - /// The value is a JavaScript ArrayBuffer object value + /// The value is a JavaScript ArrayBuffer object value /// ArrayBuffer = 10, @@ -66,7 +66,7 @@ internal enum JsValueType TypedArray = 11, /// - /// The value is a JavaScript DataView object value + /// The value is a JavaScript DataView object value /// DataView = 12 } diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/NativeMethods.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/NativeMethods.cs index 01b63156..fca07c0a 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/NativeMethods.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/NativeMethods.cs @@ -1,334 +1,376 @@ -namespace JavaScriptEngineSwitcher.ChakraCore.JsRt -{ - using System; - using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; + +using JavaScriptEngineSwitcher.ChakraCore.Constants; +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ /// /// Native methods /// internal static class NativeMethods { - const string DllName = "ChakraCore.dll"; - - [DllImport(DllName)] - internal static extern JsErrorCode JsCreateRuntime(JsRuntimeAttributes attributes, JsThreadServiceCallback threadService, out JsRuntime runtime); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsCreateRuntime(JsRuntimeAttributes attributes, + JsThreadServiceCallback threadService, out JsRuntime runtime); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsCollectGarbage(JsRuntime handle); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsDisposeRuntime(JsRuntime handle); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetRuntimeMemoryUsage(JsRuntime runtime, out UIntPtr memoryUsage); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetRuntimeMemoryLimit(JsRuntime runtime, out UIntPtr memoryLimit); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsSetRuntimeMemoryLimit(JsRuntime runtime, UIntPtr memoryLimit); - [DllImport(DllName)] - internal static extern JsErrorCode JsSetRuntimeMemoryAllocationCallback(JsRuntime runtime, IntPtr callbackState, JsMemoryAllocationCallback allocationCallback); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsSetRuntimeMemoryAllocationCallback(JsRuntime runtime, + IntPtr callbackState, JsMemoryAllocationCallback allocationCallback); - [DllImport(DllName)] - internal static extern JsErrorCode JsSetRuntimeBeforeCollectCallback(JsRuntime runtime, IntPtr callbackState, JsBeforeCollectCallback beforeCollectCallback); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsSetRuntimeBeforeCollectCallback(JsRuntime runtime, IntPtr callbackState, + JsBeforeCollectCallback beforeCollectCallback); - [DllImport(DllName, EntryPoint = "JsAddRef")] + [DllImport(DllName.Universal, EntryPoint = "JsAddRef")] internal static extern JsErrorCode JsContextAddRef(JsContext reference, out uint count); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsAddRef(JsValue reference, out uint count); - [DllImport(DllName, EntryPoint = "JsRelease")] + [DllImport(DllName.Universal, EntryPoint = "JsRelease")] internal static extern JsErrorCode JsContextRelease(JsContext reference, out uint count); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsRelease(JsValue reference, out uint count); - [DllImport(DllName)] + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsSetObjectBeforeCollectCallback(JsValue reference, IntPtr callbackState, + JsObjectBeforeCollectCallback beforeCollectCallback); + + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsCreateContext(JsRuntime runtime, out JsContext newContext); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetCurrentContext(out JsContext currentContext); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsSetCurrentContext(JsContext context); - [DllImport(DllName)] - internal static extern JsErrorCode JsGetRuntime(JsContext context, out JsRuntime runtime); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetContextOfObject(JsValue obj, out JsContext context); - [DllImport(DllName)] - internal static extern JsErrorCode JsIdle(out uint nextIdleTick); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetContextData(JsContext context, out IntPtr data); - [DllImport(DllName, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsParseScript(string script, JsSourceContext sourceContext, string sourceUrl, out JsValue result); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsSetContextData(JsContext context, IntPtr data); - [DllImport(DllName, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsRunScript(string script, JsSourceContext sourceContext, string sourceUrl, out JsValue result); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetRuntime(JsContext context, out JsRuntime runtime); - [DllImport(DllName, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSerializeScript(string script, byte[] buffer, ref ulong bufferSize); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsIdle(out uint nextIdleTick); - [DllImport(DllName, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsParseSerializedScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceUrl, out JsValue result); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetSymbolFromPropertyId(JsPropertyId propertyId, out JsValue symbol); - [DllImport(DllName, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsRunSerializedScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceUrl, out JsValue result); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetPropertyIdType(JsPropertyId propertyId, + out JsPropertyIdType propertyIdType); - [DllImport(DllName, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetPropertyIdFromName(string name, out JsPropertyId propertyId); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetPropertyIdFromSymbol(JsValue symbol, out JsPropertyId propertyId); - [DllImport(DllName, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetPropertyNameFromId(JsPropertyId propertyId, out string name); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsCreateSymbol(JsValue description, out JsValue symbol); - [DllImport(DllName)] + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetOwnPropertySymbols(JsValue obj, out JsValue propertySymbols); + + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetUndefinedValue(out JsValue undefinedValue); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetNullValue(out JsValue nullValue); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetTrueValue(out JsValue trueValue); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetFalseValue(out JsValue falseValue); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsBoolToBoolean(bool value, out JsValue booleanValue); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsBooleanToBool(JsValue booleanValue, out bool boolValue); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsConvertValueToBoolean(JsValue value, out JsValue booleanValue); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetValueType(JsValue value, out JsValueType type); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsDoubleToNumber(double doubleValue, out JsValue value); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsIntToNumber(int intValue, out JsValue value); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsNumberToDouble(JsValue value, out double doubleValue); - [DllImport(DllName)] + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsNumberToInt(JsValue value, out int intValue); + + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsConvertValueToNumber(JsValue value, out JsValue numberValue); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetStringLength(JsValue sringValue, out int length); - [DllImport(DllName, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsPointerToString(string value, UIntPtr stringLength, out JsValue stringValue); - - [DllImport(DllName)] - internal static extern JsErrorCode JsStringToPointer(JsValue value, out IntPtr stringValue, out UIntPtr stringLength); - - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsConvertValueToString(JsValue value, out JsValue stringValue); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetGlobalObject(out JsValue globalObject); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsCreateObject(out JsValue obj); - [DllImport(DllName)] - internal static extern JsErrorCode JsCreateExternalObject(IntPtr data, JsObjectFinalizeCallback finalizeCallback, out JsValue obj); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsCreateExternalObject(IntPtr data, + JsFinalizeCallback finalizeCallback, out JsValue obj); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsConvertValueToObject(JsValue value, out JsValue obj); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetPrototype(JsValue obj, out JsValue prototypeObject); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsSetPrototype(JsValue obj, JsValue prototypeObject); - [DllImport(DllName)] + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsInstanceOf(JsValue obj, JsValue constructor, out bool result); + + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetExtensionAllowed(JsValue obj, out bool value); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsPreventExtension(JsValue obj); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetProperty(JsValue obj, JsPropertyId propertyId, out JsValue value); - [DllImport(DllName)] - internal static extern JsErrorCode JsGetOwnPropertyDescriptor(JsValue obj, JsPropertyId propertyId, out JsValue propertyDescriptor); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetOwnPropertyDescriptor(JsValue obj, JsPropertyId propertyId, + out JsValue propertyDescriptor); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetOwnPropertyNames(JsValue obj, out JsValue propertyNames); - [DllImport(DllName)] - internal static extern JsErrorCode JsSetProperty(JsValue obj, JsPropertyId propertyId, JsValue value, bool useStrictRules); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsSetProperty(JsValue obj, JsPropertyId propertyId, JsValue value, + bool useStrictRules); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsHasProperty(JsValue obj, JsPropertyId propertyId, out bool hasProperty); - [DllImport(DllName)] - internal static extern JsErrorCode JsDeleteProperty(JsValue obj, JsPropertyId propertyId, bool useStrictRules, out JsValue result); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsHasOwnProperty(JsValue obj, JsPropertyId propertyId, out bool hasOwnProperty); + + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsDeleteProperty(JsValue obj, JsPropertyId propertyId, bool useStrictRules, + out JsValue result); - [DllImport(DllName)] - internal static extern JsErrorCode JsDefineProperty(JsValue obj, JsPropertyId propertyId, JsValue propertyDescriptor, out bool result); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsDefineProperty(JsValue obj, JsPropertyId propertyId, + JsValue propertyDescriptor, out bool result); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsHasIndexedProperty(JsValue obj, JsValue index, out bool result); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetIndexedProperty(JsValue obj, JsValue index, out JsValue result); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsSetIndexedProperty(JsValue obj, JsValue index, JsValue value); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsDeleteIndexedProperty(JsValue obj, JsValue index); - [DllImport(DllName)] + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsHasIndexedPropertiesExternalData(JsValue obj, out bool value); + + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetIndexedPropertiesExternalData(JsValue obj, IntPtr data, + out JsTypedArrayType arrayType, out uint elementLength); + + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsSetIndexedPropertiesToExternalData(JsValue obj, IntPtr data, + JsTypedArrayType arrayType, uint elementLength); + + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsEquals(JsValue obj1, JsValue obj2, out bool result); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsStrictEquals(JsValue obj1, JsValue obj2, out bool result); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsHasExternalData(JsValue obj, out bool value); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetExternalData(JsValue obj, out IntPtr externalData); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsSetExternalData(JsValue obj, IntPtr externalData); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsCreateArray(uint length, out JsValue result); - [DllImport(DllName)] - internal static extern JsErrorCode JsCallFunction(JsValue function, JsValue[] arguments, ushort argumentCount, out JsValue result); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsCreateArrayBuffer(uint byteLength, out JsValue result); + + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsCreateExternalArrayBuffer(IntPtr data, uint byteLength, + JsFinalizeCallback finalizeCallback, IntPtr callbackState, out JsValue result); + + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsCreateTypedArray(JsTypedArrayType arrayType, JsValue arrayBuffer, + uint byteOffset, uint elementLength, out JsValue result); - [DllImport(DllName)] - internal static extern JsErrorCode JsConstructObject(JsValue function, JsValue[] arguments, ushort argumentCount, out JsValue result); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsCreateDataView(JsValue arrayBuffer, uint byteOffset, + uint byteOffsetLength, out JsValue result); - [DllImport(DllName)] - internal static extern JsErrorCode JsCreateFunction(JsNativeFunction nativeFunction, IntPtr externalData, out JsValue function); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetTypedArrayInfo(JsValue typedArray, out JsTypedArrayType arrayType, + out JsValue arrayBuffer, out uint byteOffset, out uint byteLength); - [DllImport(DllName)] + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetArrayBufferStorage(JsValue arrayBuffer, out IntPtr buffer, + out uint bufferLength); + + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetTypedArrayStorage(JsValue typedArray, out byte[] buffer, + out uint bufferLength, out JsTypedArrayType arrayType, out int elementSize); + + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetDataViewStorage(JsValue dataView, out byte[] buffer, + out uint bufferLength); + + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsCallFunction(JsValue function, JsValue[] arguments, + ushort argumentCount, out JsValue result); + + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsConstructObject(JsValue function, JsValue[] arguments, + ushort argumentCount, out JsValue result); + + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsCreateFunction(JsNativeFunction nativeFunction, IntPtr externalData, + out JsValue function); + + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsCreateNamedFunction(JsValue name, JsNativeFunction nativeFunction, + IntPtr callbackState, out JsValue function); + + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsCreateError(JsValue message, out JsValue error); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsCreateRangeError(JsValue message, out JsValue error); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsCreateReferenceError(JsValue message, out JsValue error); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsCreateSyntaxError(JsValue message, out JsValue error); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsCreateTypeError(JsValue message, out JsValue error); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsCreateURIError(JsValue message, out JsValue error); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsHasException(out bool hasException); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsGetAndClearException(out JsValue exception); - [DllImport(DllName)] + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsGetAndClearExceptionWithMetadata(out JsValue metadata); + + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsSetException(JsValue exception); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsDisableRuntimeExecution(JsRuntime runtime); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsEnableRuntimeExecution(JsRuntime runtime); - [DllImport(DllName)] + [DllImport(DllName.Universal)] internal static extern JsErrorCode JsIsRuntimeExecutionDisabled(JsRuntime runtime, out bool isDisabled); - [DllImport(DllName)] - internal static extern JsErrorCode JsSetObjectBeforeCollectCallback(JsValue reference, IntPtr callbackState, JsObjectBeforeCollectCallback beforeCollectCallback); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsSetPromiseContinuationCallback( + JsPromiseContinuationCallback promiseContinuationCallback, IntPtr callbackState); - [DllImport(DllName)] - internal static extern JsErrorCode JsCreateNamedFunction(JsValue name, JsNativeFunction nativeFunction, IntPtr callbackState, out JsValue function); + #region Hosting - [DllImport(DllName)] - internal static extern JsErrorCode JsSetPromiseContinuationCallback(JsPromiseContinuationCallback promiseContinuationCallback, IntPtr callbackState); + [DllImport(DllName.Universal, CharSet = CharSet.Ansi)] + internal static extern JsErrorCode JsCreateString(string content, UIntPtr length, out JsValue value); - [DllImport(DllName)] - internal static extern JsErrorCode JsCreateArrayBuffer(uint byteLength, out JsValue result); - - [DllImport(DllName)] - internal static extern JsErrorCode JsCreateTypedArray(JsTypedArrayType arrayType, JsValue arrayBuffer, uint byteOffset, - uint elementLength, out JsValue result); - - [DllImport(DllName)] - internal static extern JsErrorCode JsCreateDataView(JsValue arrayBuffer, uint byteOffset, uint byteOffsetLength, out JsValue result); - - [DllImport(DllName)] - internal static extern JsErrorCode JsGetArrayBufferStorage(JsValue arrayBuffer, out byte[] buffer, out uint bufferLength); + [DllImport(DllName.Universal, CharSet = CharSet.Unicode)] + internal static extern JsErrorCode JsCreateStringUtf16(string content, UIntPtr length, out JsValue value); - [DllImport(DllName)] - internal static extern JsErrorCode JsGetTypedArrayStorage(JsValue typedArray, out byte[] buffer, out uint bufferLength, out JsTypedArrayType arrayType, out int elementSize); + [DllImport(DllName.Universal, CharSet = CharSet.Ansi)] + internal static extern JsErrorCode JsCopyString(JsValue value, byte[] buffer, UIntPtr bufferSize, + out UIntPtr length); - [DllImport(DllName)] - internal static extern JsErrorCode JsGetDataViewStorage(JsValue dataView, out byte[] buffer, out uint bufferLength); + [DllImport(DllName.Universal, CharSet = CharSet.Unicode)] + internal static extern JsErrorCode JsCopyStringUtf16(JsValue value, int start, int length, + char[] buffer, out UIntPtr written); - [DllImport(DllName)] - internal static extern JsErrorCode JsGetPropertyIdType(JsPropertyId propertyId, out JsPropertyIdType propertyIdType); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsParse(JsValue script, JsSourceContext sourceContext, JsValue sourceUrl, + JsParseScriptAttributes parseAttributes, out JsValue result); - [DllImport(DllName)] - internal static extern JsErrorCode JsCreateSymbol(JsValue description, out JsValue symbol); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsRun(JsValue script, JsSourceContext sourceContext, JsValue sourceUrl, + JsParseScriptAttributes parseAttributes, out JsValue result); - [DllImport(DllName)] - internal static extern JsErrorCode JsGetSymbolFromPropertyId(JsPropertyId propertyId, out JsValue symbol); + [DllImport(DllName.Universal, CharSet = CharSet.Ansi)] + internal static extern JsErrorCode JsCreatePropertyId(string name, UIntPtr length, + out JsPropertyId propertyId); - [DllImport(DllName)] - internal static extern JsErrorCode JsGetPropertyIdFromSymbol(JsValue symbol, out JsPropertyId propertyId); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsCopyPropertyId(JsPropertyId propertyId, byte[] buffer, + UIntPtr bufferSize, out UIntPtr length); - [DllImport(DllName)] - internal static extern JsErrorCode JsGetOwnPropertySymbols(JsValue obj, out JsValue propertySymbols); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsSerialize(JsValue script, out JsValue buffer, + JsParseScriptAttributes parseAttributes); - [DllImport(DllName)] - internal static extern JsErrorCode JsNumberToInt(JsValue value, out int intValue); - - [DllImport(DllName)] - internal static extern JsErrorCode JsSetIndexedPropertiesToExternalData(JsValue obj, IntPtr data, JsTypedArrayType arrayType, uint elementLength); - - [DllImport(DllName)] - internal static extern JsErrorCode JsGetIndexedPropertiesExternalData(JsValue obj, IntPtr data, out JsTypedArrayType arrayType, out uint elementLength); - - [DllImport(DllName)] - internal static extern JsErrorCode JsHasIndexedPropertiesExternalData(JsValue obj, out bool value); - - [DllImport(DllName)] - internal static extern JsErrorCode JsInstanceOf(JsValue obj, JsValue constructor, out bool result); - - [DllImport(DllName)] - internal static extern JsErrorCode JsCreateExternalArrayBuffer(IntPtr data, uint byteLength, JsObjectFinalizeCallback finalizeCallback, IntPtr callbackState, out bool result); - - [DllImport(DllName)] - internal static extern JsErrorCode JsGetTypedArrayInfo(JsValue typedArray, out JsTypedArrayType arrayType, out JsValue arrayBuffer, out uint byteOffset, out uint byteLength); - - [DllImport(DllName)] - internal static extern JsErrorCode JsGetContextOfObject(JsValue obj, out JsContext context); - - [DllImport(DllName)] - internal static extern JsErrorCode JsGetContextData(JsContext context, out IntPtr data); - - [DllImport(DllName)] - internal static extern JsErrorCode JsSetContextData(JsContext context, IntPtr data); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsParseSerialized(JsValue buffer, + JsSerializedLoadScriptCallback scriptLoadCallback, JsSourceContext sourceContext, + JsValue sourceUrl, out JsValue result); - [DllImport(DllName)] - internal static extern JsErrorCode JsParseSerializedScriptWithCallback(JsSerializedScriptLoadSourceCallback scriptLoadCallback, - JsSerializedScriptUnloadCallback scriptUnloadCallback, byte[] buffer, JsSourceContext sourceContext, string sourceUrl, out JsValue result); + [DllImport(DllName.Universal)] + internal static extern JsErrorCode JsRunSerialized(JsValue buffer, + JsSerializedLoadScriptCallback scriptLoadCallback, JsSourceContext sourceContext, + JsValue sourceUrl, out JsValue result); - [DllImport(DllName)] - internal static extern JsErrorCode JsRunSerializedScriptWithCallback(JsSerializedScriptLoadSourceCallback scriptLoadCallback, - JsSerializedScriptUnloadCallback scriptUnloadCallback, byte[] buffer, JsSourceContext sourceContext, string sourceUrl, out JsValue result); + #endregion } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/TypeMapper.cs b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/TypeMapper.cs new file mode 100644 index 00000000..3fef5501 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/JsRt/TypeMapper.cs @@ -0,0 +1,1091 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Extensions; +using JavaScriptEngineSwitcher.Core.Utilities; + +using CoreErrorHelpers = JavaScriptEngineSwitcher.Core.Helpers.JsErrorHelpers; +using WrapperException = JavaScriptEngineSwitcher.Core.JsException; +using WrapperRuntimeException = JavaScriptEngineSwitcher.Core.JsRuntimeException; +using WrapperScriptException = JavaScriptEngineSwitcher.Core.JsScriptException; + +using JavaScriptEngineSwitcher.ChakraCore.Helpers; +using JavaScriptEngineSwitcher.ChakraCore.JsRt.Embedding; +using JavaScriptEngineSwitcher.ChakraCore.Resources; + +namespace JavaScriptEngineSwitcher.ChakraCore.JsRt +{ + /// + /// Type mapper + /// + internal sealed class TypeMapper : IDisposable + { + /// + /// Name of property to store the external object + /// + private const string ExternalObjectPropertyName = "_JavaScriptEngineSwitcher_externalObject"; + + /// + /// Flag for whether to allow the usage of reflection API in the script code + /// + private readonly bool _allowReflection; + + /// + /// Storage for lazy-initialized embedded objects + /// + private ConcurrentDictionary> _lazyEmbeddedObjects; + + /// + /// Callback for finalization of embedded object + /// + private JsFinalizeCallback _embeddedObjectFinalizeCallback; + + /// + /// Synchronizer of embedded object storage's initialization + /// + private readonly object _embeddedObjectStorageInitializationSynchronizer = new object(); + + /// + /// Flag indicating whether the embedded object storage is initialized + /// + private bool _embeddedObjectStorageInitialized; + + /// + /// Storage for lazy-initialized embedded types + /// + private ConcurrentDictionary> _lazyEmbeddedTypes; + + /// + /// Callback for finalization of embedded type + /// + private JsFinalizeCallback _embeddedTypeFinalizeCallback; + + /// + /// Synchronizer of embedded type storage's initialization + /// + private readonly object _embeddedTypeStorageInitializationSynchronizer = new object(); + + /// + /// Flag indicating whether the embedded type storage is initialized + /// + private bool _embeddedTypeStorageInitialized; + + /// + /// Flag indicating whether this object is disposed + /// + private InterlockedStatedFlag _disposedFlag = new InterlockedStatedFlag(); + + + /// + /// Constructs an instance of type mapper + /// + /// Flag for whether to allow the usage of reflection API in the script code + public TypeMapper(bool allowReflection) + { + _allowReflection = allowReflection; + } + + + /// + /// Creates a JavaScript value from an host object if the it does not already exist + /// + /// Instance of host type + /// JavaScript value created from an host object + public JsValue GetOrCreateScriptObject(object obj) + { + if (!_embeddedObjectStorageInitialized) + { + lock (_embeddedObjectStorageInitializationSynchronizer) + { + if (!_embeddedObjectStorageInitialized) + { + _lazyEmbeddedObjects = new ConcurrentDictionary>(); + _embeddedObjectFinalizeCallback = EmbeddedObjectFinalizeCallback; + + _embeddedObjectStorageInitialized = true; + } + } + } + + var embeddedObjectKey = new EmbeddedObjectKey(obj); + EmbeddedObject embeddedObject = _lazyEmbeddedObjects.GetOrAdd( + embeddedObjectKey, + key => new Lazy(() => CreateEmbeddedObjectOrFunction(obj)) + ).Value; + + return embeddedObject.ScriptValue; + } + + /// + /// Creates a JavaScript value from an host type if the it does not already exist + /// + /// Host type + /// JavaScript value created from an host type + public JsValue GetOrCreateScriptType(Type type) + { + if (!_embeddedTypeStorageInitialized) + { + lock (_embeddedTypeStorageInitializationSynchronizer) + { + if (!_embeddedTypeStorageInitialized) + { + _lazyEmbeddedTypes = new ConcurrentDictionary>(); + _embeddedTypeFinalizeCallback = EmbeddedTypeFinalizeCallback; + + _embeddedTypeStorageInitialized = true; + } + } + } + + string embeddedTypeKey = type.AssemblyQualifiedName; + EmbeddedType embeddedType = _lazyEmbeddedTypes.GetOrAdd( + embeddedTypeKey, + key => new Lazy(() => CreateEmbeddedType(type)) + ).Value; + + return embeddedType.ScriptValue; + } + + /// + /// Makes a mapping of value from the host type to a script type + /// + /// The source value + /// The mapped value + public JsValue MapToScriptType(object value) + { + if (value == null) + { + return JsValue.Null; + } + + if (value is Undefined) + { + return JsValue.Undefined; + } + + TypeCode typeCode = value.GetType().GetTypeCode(); + + switch (typeCode) + { + case TypeCode.Boolean: + return (bool)value ? JsValue.True : JsValue.False; + + case TypeCode.SByte: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + return JsValue.FromInt32(Convert.ToInt32(value)); + + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return JsValue.FromDouble(Convert.ToDouble(value)); + + case TypeCode.Char: + case TypeCode.String: + return JsValue.FromString((string)value); + + default: + return value is JsValue ? (JsValue)value : GetOrCreateScriptObject(value); + } + } + + /// + /// Makes a mapping of value from the script type to a host type + /// + /// The source value + /// The mapped value + public object MapToHostType(JsValue value) + { + JsValueType valueType = value.ValueType; + object result = null; + + switch (valueType) + { + case JsValueType.Null: + result = null; + break; + case JsValueType.Undefined: + result = Undefined.Value; + break; + case JsValueType.Boolean: + result = value.ToBoolean(); + break; + case JsValueType.Number: + result = NumericHelpers.CastDoubleValueToCorrectType(value.ToDouble()); + break; + case JsValueType.String: + result = value.ToString(); + break; + case JsValueType.Function: + JsPropertyId externalObjectPropertyId = JsPropertyId.FromString(ExternalObjectPropertyName); + if (value.HasProperty(externalObjectPropertyId)) + { + JsValue externalObjectValue = value.GetProperty(externalObjectPropertyId); + result = externalObjectValue.HasExternalData ? + GCHandle.FromIntPtr(externalObjectValue.ExternalData).Target : null; + } + + result = result ?? value.ConvertToObject(); + break; + case JsValueType.Object: + case JsValueType.Error: + case JsValueType.Array: + case JsValueType.Symbol: + case JsValueType.ArrayBuffer: + case JsValueType.TypedArray: + case JsValueType.DataView: + result = value.HasExternalData ? + GCHandle.FromIntPtr(value.ExternalData).Target : value.ConvertToObject(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + return result; + } + + private EmbeddedObject CreateEmbeddedObjectOrFunction(object obj) + { + var del = obj as Delegate; + EmbeddedObject embeddedObject = del != null ? + CreateEmbeddedFunction(del) : CreateEmbeddedObject(obj); + + return embeddedObject; + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private EmbeddedObject CreateEmbeddedObject(object obj) + { + GCHandle objHandle = GCHandle.Alloc(obj); + IntPtr objPtr = GCHandle.ToIntPtr(objHandle); + JsValue objValue = JsValue.CreateExternalObject(objPtr, _embeddedObjectFinalizeCallback); + + var embeddedObject = new EmbeddedObject(obj, objValue); + + ProjectFields(embeddedObject); + ProjectProperties(embeddedObject); + ProjectMethods(embeddedObject); + FreezeObject(objValue); + + return embeddedObject; + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private EmbeddedObject CreateEmbeddedFunction(Delegate del) + { + JsNativeFunction nativeFunction = (callee, isConstructCall, args, argCount, callbackData) => + { +#if NET40 + MethodInfo method = del.Method; +#else + MethodInfo method = del.GetMethodInfo(); +#endif + ParameterInfo[] parameters = method.GetParameters(); + object[] processedArgs = GetHostItemMemberArguments(args, parameters.Length); + + ReflectionHelpers.FixArgumentTypes(ref processedArgs, parameters); + + object result; + + try + { + result = del.DynamicInvoke(processedArgs); + } + catch (Exception e) + { + JsValue undefinedValue = JsValue.Undefined; + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + JsValue errorValue = wrapperException != null ? + CreateErrorFromWrapperException(wrapperException) + : + JsErrorHelpers.CreateError(string.Format( + Strings.Runtime_HostDelegateInvocationFailed, exception.Message)) + ; + JsContext.SetException(errorValue); + + return undefinedValue; + } + + JsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + + GCHandle delHandle = GCHandle.Alloc(del); + IntPtr delPtr = GCHandle.ToIntPtr(delHandle); + JsValue objValue = JsValue.CreateExternalObject(delPtr, _embeddedObjectFinalizeCallback); + + JsValue functionValue = JsValue.CreateFunction(nativeFunction); + SetNonEnumerableProperty(functionValue, ExternalObjectPropertyName, objValue); + + var embeddedObject = new EmbeddedObject(del, functionValue, + new List { nativeFunction }); + + return embeddedObject; + } + + private void EmbeddedObjectFinalizeCallback(IntPtr ptr) + { + if (ptr == IntPtr.Zero) + { + return; + } + + GCHandle objHandle = GCHandle.FromIntPtr(ptr); + object obj = objHandle.Target; + var lazyEmbeddedObjects = _lazyEmbeddedObjects; + + if (obj != null && lazyEmbeddedObjects != null) + { + var embeddedObjectKey = new EmbeddedObjectKey(obj); + Lazy lazyEmbeddedObject; + + if (lazyEmbeddedObjects.TryRemove(embeddedObjectKey, out lazyEmbeddedObject)) + { + lazyEmbeddedObject.Value?.Dispose(); + } + } + + objHandle.Free(); + } + + private EmbeddedType CreateEmbeddedType(Type type) + { +#if NET40 + Type typeInfo = type; +#else + TypeInfo typeInfo = type.GetTypeInfo(); +#endif + string typeName = type.FullName; + BindingFlags defaultBindingFlags = ReflectionHelpers.GetDefaultBindingFlags(true); + ConstructorInfo[] constructors = type.GetConstructors(defaultBindingFlags); + + JsNativeFunction nativeConstructorFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + object result; + JsValue resultValue; + object[] processedArgs = GetHostItemMemberArguments(args); + + if (processedArgs.Length == 0 && typeInfo.IsValueType) + { + result = Activator.CreateInstance(type); + resultValue = MapToScriptType(result); + + return resultValue; + } + + JsValue undefinedValue = JsValue.Undefined; + + if (constructors.Length == 0) + { + CreateAndSetError(string.Format(Strings.Runtime_HostTypeConstructorNotFound, typeName)); + return undefinedValue; + } + + var bestFitConstructor = (ConstructorInfo)ReflectionHelpers.GetBestFitMethod( + constructors, processedArgs); + if (bestFitConstructor == null) + { + CreateAndSetReferenceError(string.Format( + Strings.Runtime_SuitableConstructorOfHostTypeNotFound, typeName)); + return undefinedValue; + } + + ReflectionHelpers.FixArgumentTypes(ref processedArgs, bestFitConstructor.GetParameters()); + + try + { + result = bestFitConstructor.Invoke(processedArgs); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + JsValue errorValue = wrapperException != null ? + CreateErrorFromWrapperException(wrapperException) + : + JsErrorHelpers.CreateError(string.Format( + Strings.Runtime_HostTypeConstructorInvocationFailed, typeName, exception.Message)) + ; + JsContext.SetException(errorValue); + + return undefinedValue; + } + + resultValue = MapToScriptType(result); + + return resultValue; + }; + + GCHandle embeddedTypeHandle = GCHandle.Alloc(type); + IntPtr embeddedTypePtr = GCHandle.ToIntPtr(embeddedTypeHandle); + JsValue objValue = JsValue.CreateExternalObject(embeddedTypePtr, _embeddedTypeFinalizeCallback); + + JsValue typeValue = JsValue.CreateFunction(nativeConstructorFunction); + SetNonEnumerableProperty(typeValue, ExternalObjectPropertyName, objValue); + + var embeddedType = new EmbeddedType(type, typeValue, + new List { nativeConstructorFunction }); + + ProjectFields(embeddedType); + ProjectProperties(embeddedType); + ProjectMethods(embeddedType); + FreezeObject(typeValue); + + return embeddedType; + } + + private void EmbeddedTypeFinalizeCallback(IntPtr ptr) + { + if (ptr == IntPtr.Zero) + { + return; + } + + GCHandle embeddedTypeHandle = GCHandle.FromIntPtr(ptr); + var type = (Type)embeddedTypeHandle.Target; + string embeddedTypeKey = type.AssemblyQualifiedName; + var lazyEmbeddedTypes = _lazyEmbeddedTypes; + + if (!string.IsNullOrEmpty(embeddedTypeKey) && lazyEmbeddedTypes != null) + { + Lazy lazyEmbeddedType; + + if (lazyEmbeddedTypes.TryRemove(embeddedTypeKey, out lazyEmbeddedType)) + { + lazyEmbeddedType.Value?.Dispose(); + } + } + + embeddedTypeHandle.Free(); + } + + private void ProjectFields(EmbeddedItem externalItem) + { + Type type = externalItem.HostType; + object obj = externalItem.HostObject; + JsValue typeValue = externalItem.ScriptValue; + bool instance = externalItem.IsInstance; + IList nativeFunctions = externalItem.NativeFunctions; + + string typeName = type.FullName; + BindingFlags defaultBindingFlags = ReflectionHelpers.GetDefaultBindingFlags(instance); + FieldInfo[] fields = type.GetFields(defaultBindingFlags); + + foreach (FieldInfo field in fields) + { + string fieldName = field.Name; + + JsValue descriptorValue = JsValue.CreateObject(); + descriptorValue.SetProperty("enumerable", JsValue.True, true); + + JsNativeFunction nativeGetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + JsValue undefinedValue = JsValue.Undefined; + + if (instance && obj == null) + { + CreateAndSetTypeError(string.Format( + Strings.Runtime_InvalidThisContextForHostObjectField, fieldName)); + return undefinedValue; + } + + object result; + + try + { + result = field.GetValue(obj); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + JsValue errorValue; + + if (wrapperException != null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(Strings.Runtime_HostObjectFieldGettingFailed, fieldName, + exception.Message) + : + string.Format(Strings.Runtime_HostTypeFieldGettingFailed, fieldName, typeName, + exception.Message) + ; + errorValue = JsErrorHelpers.CreateError(errorMessage); + } + JsContext.SetException(errorValue); + + return undefinedValue; + } + + JsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + nativeFunctions.Add(nativeGetFunction); + + JsValue getMethodValue = JsValue.CreateFunction(nativeGetFunction); + descriptorValue.SetProperty("get", getMethodValue, true); + + JsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + JsValue undefinedValue = JsValue.Undefined; + + if (instance && obj == null) + { + CreateAndSetTypeError(string.Format( + Strings.Runtime_InvalidThisContextForHostObjectField, fieldName)); + return undefinedValue; + } + + object value = MapToHostType(args[1]); + ReflectionHelpers.FixFieldValueType(ref value, field); + + try + { + field.SetValue(obj, value); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + JsValue errorValue; + + if (wrapperException != null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(Strings.Runtime_HostObjectFieldSettingFailed, fieldName, + exception.Message) + : + string.Format(Strings.Runtime_HostTypeFieldSettingFailed, fieldName, typeName, + exception.Message) + ; + errorValue = JsErrorHelpers.CreateError(errorMessage); + } + JsContext.SetException(errorValue); + + return undefinedValue; + } + + return undefinedValue; + }; + nativeFunctions.Add(nativeSetFunction); + + JsValue setMethodValue = JsValue.CreateFunction(nativeSetFunction); + descriptorValue.SetProperty("set", setMethodValue, true); + + typeValue.DefineProperty(fieldName, descriptorValue); + } + } + + private void ProjectProperties(EmbeddedItem externalItem) + { + Type type = externalItem.HostType; + object obj = externalItem.HostObject; + JsValue typeValue = externalItem.ScriptValue; + IList nativeFunctions = externalItem.NativeFunctions; + bool instance = externalItem.IsInstance; + + string typeName = type.FullName; + BindingFlags defaultBindingFlags = ReflectionHelpers.GetDefaultBindingFlags(instance); + PropertyInfo[] properties = type.GetProperties(defaultBindingFlags); + + foreach (PropertyInfo property in properties) + { + if (!IsAvailableProperty(property)) + { + continue; + } + + string propertyName = property.Name; + + JsValue descriptorValue = JsValue.CreateObject(); + descriptorValue.SetProperty("enumerable", JsValue.True, true); + + if (property.GetGetMethod() != null) + { + JsNativeFunction nativeGetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + JsValue undefinedValue = JsValue.Undefined; + + if (instance && obj == null) + { + CreateAndSetTypeError(string.Format( + Strings.Runtime_InvalidThisContextForHostObjectProperty, propertyName)); + return undefinedValue; + } + + object result; + + try + { + result = property.GetValue(obj, new object[0]); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + JsValue errorValue; + + if (wrapperException != null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(Strings.Runtime_HostObjectPropertyGettingFailed, propertyName, + exception.Message) + : + string.Format(Strings.Runtime_HostTypePropertyGettingFailed, propertyName, + typeName, exception.Message) + ; + errorValue = JsErrorHelpers.CreateError(errorMessage); + } + JsContext.SetException(errorValue); + + return undefinedValue; + } + + JsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + nativeFunctions.Add(nativeGetFunction); + + JsValue getMethodValue = JsValue.CreateFunction(nativeGetFunction); + descriptorValue.SetProperty("get", getMethodValue, true); + } + + if (property.GetSetMethod() != null) + { + JsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + JsValue undefinedValue = JsValue.Undefined; + + if (instance && obj == null) + { + CreateAndSetTypeError(string.Format( + Strings.Runtime_InvalidThisContextForHostObjectProperty, propertyName)); + return undefinedValue; + } + + object value = MapToHostType(args[1]); + ReflectionHelpers.FixPropertyValueType(ref value, property); + + try + { + property.SetValue(obj, value, new object[0]); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + JsValue errorValue; + + if (wrapperException != null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(Strings.Runtime_HostObjectPropertySettingFailed, propertyName, + exception.Message) + : + string.Format(Strings.Runtime_HostTypePropertySettingFailed, propertyName, + typeName, exception.Message) + ; + errorValue = JsErrorHelpers.CreateError(errorMessage); + } + JsContext.SetException(errorValue); + + return undefinedValue; + } + + return undefinedValue; + }; + nativeFunctions.Add(nativeSetFunction); + + JsValue setMethodValue = JsValue.CreateFunction(nativeSetFunction); + descriptorValue.SetProperty("set", setMethodValue, true); + } + + typeValue.DefineProperty(propertyName, descriptorValue); + } + } + + private void ProjectMethods(EmbeddedItem externalItem) + { + Type type = externalItem.HostType; + object obj = externalItem.HostObject; + JsValue typeValue = externalItem.ScriptValue; + IList nativeFunctions = externalItem.NativeFunctions; + bool instance = externalItem.IsInstance; + + string typeName = type.FullName; + BindingFlags defaultBindingFlags = ReflectionHelpers.GetDefaultBindingFlags(instance); + MethodInfo[] methods = type.GetMethods(defaultBindingFlags); + Dictionary> availableMethodGroups = GetAvailableMethodGroups(methods); + + foreach (KeyValuePair> methodGroup in availableMethodGroups) + { + string methodName = methodGroup.Key; + MethodInfo[] methodCandidates = methodGroup.Value.ToArray(); + + JsNativeFunction nativeFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + JsValue undefinedValue = JsValue.Undefined; + + if (instance && obj == null) + { + CreateAndSetTypeError(string.Format( + Strings.Runtime_InvalidThisContextForHostObjectMethod, methodName)); + return undefinedValue; + } + + object[] processedArgs = GetHostItemMemberArguments(args); + + var bestFitMethod = (MethodInfo)ReflectionHelpers.GetBestFitMethod( + methodCandidates, processedArgs); + if (bestFitMethod == null) + { + CreateAndSetReferenceError(string.Format( + Strings.Runtime_SuitableMethodOfHostObjectNotFound, methodName)); + return undefinedValue; + } + + ReflectionHelpers.FixArgumentTypes(ref processedArgs, bestFitMethod.GetParameters()); + + object result; + + try + { + result = bestFitMethod.Invoke(obj, processedArgs); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + JsValue errorValue; + + if (wrapperException != null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(Strings.Runtime_HostObjectMethodInvocationFailed, methodName, + exception.Message) + : + string.Format(Strings.Runtime_HostTypeMethodInvocationFailed, methodName, typeName, + exception.Message) + ; + errorValue = JsErrorHelpers.CreateError(errorMessage); + } + JsContext.SetException(errorValue); + + return undefinedValue; + } + + JsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + nativeFunctions.Add(nativeFunction); + + JsValue methodValue = JsValue.CreateFunction(nativeFunction); + typeValue.SetProperty(methodName, methodValue, true); + } + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private bool IsAvailableProperty(PropertyInfo property) + { + if (_allowReflection) + { + return true; + } + + bool isAvailable = ReflectionHelpers.IsAllowedProperty(property); + + return isAvailable; + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private Dictionary> GetAvailableMethodGroups(MethodInfo[] methods) + { + int methodCount = methods.Length; + if (methodCount == 0) + { + return new Dictionary>(); + } + + var availableMethodGroups = new Dictionary>(methodCount); + + foreach (MethodInfo method in methods) + { + if (!ReflectionHelpers.IsFullyFledgedMethod(method) + || (!_allowReflection && !ReflectionHelpers.IsAllowedMethod(method))) + { + continue; + } + + string methodName = method.Name; + List methodGroup; + + if (availableMethodGroups.TryGetValue(methodName, out methodGroup)) + { + methodGroup.Add(method); + } + else + { + methodGroup = new List { method }; + availableMethodGroups.Add(methodName, methodGroup); + } + } + + return availableMethodGroups; + } + + private object[] GetHostItemMemberArguments(JsValue[] args, int maxArgCount = -1) + { + if (args == null) + { + throw new ArgumentNullException(nameof(args)); + } + + int argCount = args.Length; + const int skippedArgCount = 1; + int processedArgCount = argCount >= skippedArgCount ? argCount - skippedArgCount : 0; + if (maxArgCount >= 0 && processedArgCount > maxArgCount) + { + processedArgCount = maxArgCount; + } + object[] processedArgs; + + if (processedArgCount > 0) + { + processedArgs = args + .Skip(skippedArgCount) + .Take(processedArgCount) + .Select(MapToHostType) + .ToArray() + ; + } + else + { + processedArgs = new object[0]; + } + + return processedArgs; + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void FreezeObject(JsValue objValue) + { + JsValue freezeMethodValue = JsValue.GlobalObject + .GetProperty("Object") + .GetProperty("freeze") + ; + freezeMethodValue.CallFunction(objValue); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void SetNonEnumerableProperty(JsValue objValue, string name, JsValue value) + { + JsValue descriptorValue = JsValue.CreateObject(); + descriptorValue.SetProperty("enumerable", JsValue.False, true); + descriptorValue.SetProperty("writable", JsValue.True, true); + + JsPropertyId id = JsPropertyId.FromString(name); + objValue.DefineProperty(id, descriptorValue); + objValue.SetProperty(id, value, true); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void CreateAndSetError(string message) + { + JsValue errorValue = JsErrorHelpers.CreateError(message); + JsContext.SetException(errorValue); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void CreateAndSetReferenceError(string message) + { + JsValue errorValue = JsErrorHelpers.CreateReferenceError(message); + JsContext.SetException(errorValue); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void CreateAndSetTypeError(string message) + { + JsValue errorValue = JsErrorHelpers.CreateTypeError(message); + JsContext.SetException(errorValue); + } + + private static Exception UnwrapException(Exception exception) + { + Exception originalException = exception; + var targetInvocationException = exception as TargetInvocationException; + + if (targetInvocationException != null) + { + Exception innerException = targetInvocationException.InnerException; + if (innerException != null) + { + originalException = innerException; + } + } + + return originalException; + } + + private static JsValue CreateErrorFromWrapperException(WrapperException exception) + { + var originalException = exception.InnerException as JsException; + JsErrorCode errorCode = originalException != null ? + originalException.ErrorCode : JsErrorCode.NoError; + string description = exception.Description; + + JsValue innerErrorValue = JsErrorHelpers.CreateError(description); + innerErrorValue.SetProperty("description", JsValue.FromString(description), true); + + JsValue metadataValue = JsValue.CreateObject(); + + var scriptException = exception as WrapperScriptException; + if (scriptException != null) + { + string type = scriptException.Type; + string documentName = scriptException.DocumentName; + int lineNumber = scriptException.LineNumber; + if (lineNumber > 0) + { + lineNumber--; + } + int columnNumber = scriptException.ColumnNumber; + if (columnNumber > 0) + { + columnNumber--; + } + string sourceFragment = scriptException.SourceFragment; + + innerErrorValue.SetProperty("name", JsValue.FromString(type), true); + + var runtimeException = scriptException as WrapperRuntimeException; + if (runtimeException != null) + { + var errorNumber = (int)errorCode; + string callStack = runtimeException.CallStack; + string messageWithTypeAndCallStack = CoreErrorHelpers.GenerateScriptErrorMessage(type, + description, callStack); + + innerErrorValue.SetProperty("number", JsValue.FromInt32(errorNumber), true); + if (!string.IsNullOrWhiteSpace(callStack)) + { + innerErrorValue.SetProperty("stack", JsValue.FromString(messageWithTypeAndCallStack), true); + } + } + else + { + innerErrorValue.SetProperty("url", JsValue.FromString(documentName), true); + innerErrorValue.SetProperty("line", JsValue.FromInt32(lineNumber), true); + innerErrorValue.SetProperty("column", JsValue.FromInt32(columnNumber), true); + innerErrorValue.SetProperty("source", JsValue.FromString(sourceFragment), true); + } + + metadataValue.SetProperty("url", JsValue.FromString(documentName), true); + metadataValue.SetProperty("line", JsValue.FromInt32(lineNumber), true); + metadataValue.SetProperty("column", JsValue.FromInt32(columnNumber), true); + metadataValue.SetProperty("source", JsValue.FromString(sourceFragment), true); + } + + innerErrorValue.SetProperty("metadata", metadataValue, true); + + JsValue errorValue = JsErrorHelpers.CreateError(description); + errorValue.SetProperty("innerException", innerErrorValue, true); + + return errorValue; + } + + #region IDisposable implementation + + /// + /// Disposes a type mapper + /// + public void Dispose() + { + if (_disposedFlag.Set()) + { + var lazyEmbeddedObjects = _lazyEmbeddedObjects; + if (lazyEmbeddedObjects != null) + { + if (lazyEmbeddedObjects.Count > 0) + { + foreach (EmbeddedObjectKey key in lazyEmbeddedObjects.Keys) + { + Lazy lazyEmbeddedObject; + + if (lazyEmbeddedObjects.TryGetValue(key, out lazyEmbeddedObject)) + { + lazyEmbeddedObject.Value?.Dispose(); + } + } + + lazyEmbeddedObjects.Clear(); + } + + _lazyEmbeddedObjects = null; + } + + _embeddedObjectFinalizeCallback = null; + + var lazyEmbeddedTypes = _lazyEmbeddedTypes; + if (lazyEmbeddedTypes != null) + { + if (lazyEmbeddedTypes.Count > 0) + { + foreach (string key in lazyEmbeddedTypes.Keys) + { + Lazy lazyEmbeddedType; + + if (lazyEmbeddedTypes.TryGetValue(key, out lazyEmbeddedType)) + { + lazyEmbeddedType.Value?.Dispose(); + } + } + + lazyEmbeddedTypes.Clear(); + } + + _lazyEmbeddedTypes = null; + } + + _embeddedTypeFinalizeCallback = null; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.ChakraCore/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..7ba63673 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/PACKAGE-DESCRIPTION.md @@ -0,0 +1,13 @@ +JavaScriptEngineSwitcher.ChakraCore contains a `ChakraCoreJsEngine` adapter (wrapper for the [ChakraCore](https://github.com/chakra-core/ChakraCore)). +Project was based on the code of [Chakra-Samples](https://github.com/Microsoft/Chakra-Samples) and [jsrt-dotnet](https://github.com/robpaveza/jsrt-dotnet). + +This package does not contain the native implementations of ChakraCore. +Therefore, you need to choose and install the most appropriate package(s) for your platform. +The following packages are available: + + * [JavaScriptEngineSwitcher.ChakraCore.Native.win-x86](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.win-x86) + * [JavaScriptEngineSwitcher.ChakraCore.Native.win-x64](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.win-x64) + * [JavaScriptEngineSwitcher.ChakraCore.Native.win-arm](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm) + * [JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64) + * [JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64) + * [JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64](https://www.nuget.org/packages/JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64) \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/Properties/AssemblyInfo.cs b/src/JavaScriptEngineSwitcher.ChakraCore/Properties/AssemblyInfo.cs deleted file mode 100644 index 17f1a020..00000000 --- a/src/JavaScriptEngineSwitcher.ChakraCore/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("JavaScriptEngineSwitcher.ChakraCore")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JavaScript Engine Switcher for .Net: ChakraCore")] -[assembly: AssemblyCopyright("Copyright © 2013-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("07d1fd39-469c-44ae-bf0f-4116f8887669")] - -[assembly: AssemblyVersion("1.5.4.0")] -[assembly: AssemblyFileVersion("1.5.4.0")] \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.Designer.cs b/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.Designer.cs index 19de5fe7..7547d486 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.Designer.cs +++ b/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.Designer.cs @@ -1,243 +1,251 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 +// This code was generated by a tool. // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ +namespace JavaScriptEngineSwitcher.ChakraCore.Resources +{ + using System; + using System.Globalization; + using System.Reflection; + using System.Resources; + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + internal class Strings + { + private static Lazy _resourceManager = + new Lazy(() => new ResourceManager( + "JavaScriptEngineSwitcher.ChakraCore.Resources.Strings", +#if NET20 || NET30 || NET35 || NET40 + typeof(Strings).Assembly +#else + typeof(Strings).GetTypeInfo().Assembly +#endif + )); + + private static CultureInfo _resourceCulture; + + /// + /// Returns a cached ResourceManager instance used by this class + /// + internal static ResourceManager ResourceManager + { + get + { + return _resourceManager.Value; + } + } + + /// + /// Overrides a current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class + /// + internal static CultureInfo Culture + { + get + { + return _resourceCulture; + } + set + { + _resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to "Failed to add the '{0}' directory to the search path used to locate DLLs for the application." + /// + internal static string Engines_AddingDirectoryToDllSearchPathFailed + { + get { return GetString("Engines_AddingDirectoryToDllSearchPathFailed"); } + } + + /// + /// Looks up a localized string similar to "During invocation of the host delegate an error has occurred - “{0}”." + /// + internal static string Runtime_HostDelegateInvocationFailed + { + get { return GetString("Runtime_HostDelegateInvocationFailed"); } + } + + /// + /// Looks up a localized string similar to "During getting value of '{0}' field of the host object an error has occurred - “{1}”." + /// + internal static string Runtime_HostObjectFieldGettingFailed + { + get { return GetString("Runtime_HostObjectFieldGettingFailed"); } + } + + /// + /// Looks up a localized string similar to "During setting value of '{0}' field of the host object an error has occurred - “{1}”." + /// + internal static string Runtime_HostObjectFieldSettingFailed + { + get { return GetString("Runtime_HostObjectFieldSettingFailed"); } + } + + /// + /// Looks up a localized string similar to "During invocation of '{0}' method of the host object an error has occurred - “{1}”." + /// + internal static string Runtime_HostObjectMethodInvocationFailed + { + get { return GetString("Runtime_HostObjectMethodInvocationFailed"); } + } + + /// + /// Looks up a localized string similar to "During getting value of '{0}' property of the host object an error has occurred - “{1}”." + /// + internal static string Runtime_HostObjectPropertyGettingFailed + { + get { return GetString("Runtime_HostObjectPropertyGettingFailed"); } + } + + /// + /// Looks up a localized string similar to "During setting value of '{0}' property of the host object an error has occurred - “{1}”." + /// + internal static string Runtime_HostObjectPropertySettingFailed + { + get { return GetString("Runtime_HostObjectPropertySettingFailed"); } + } + + /// + /// Looks up a localized string similar to "During invocation of constructor of the `{0}` host type an error has occurred - “{1}”." + /// + internal static string Runtime_HostTypeConstructorInvocationFailed + { + get { return GetString("Runtime_HostTypeConstructorInvocationFailed"); } + } + + /// + /// Looks up a localized string similar to "Could not create instance of the `{0}` host type, because it does not have any public constructor." + /// + internal static string Runtime_HostTypeConstructorNotFound + { + get { return GetString("Runtime_HostTypeConstructorNotFound"); } + } + + /// + /// Looks up a localized string similar to "During getting value of '{0}' field of the `{1}` host type an error has occurred - “{2}”." + /// + internal static string Runtime_HostTypeFieldGettingFailed + { + get { return GetString("Runtime_HostTypeFieldGettingFailed"); } + } + + /// + /// Looks up a localized string similar to "During setting value of '{0}' field of the `{1}` host type an error has occurred - “{2}”." + /// + internal static string Runtime_HostTypeFieldSettingFailed + { + get { return GetString("Runtime_HostTypeFieldSettingFailed"); } + } + + /// + /// Looks up a localized string similar to "During invocation of '{0}' method of the `{1}` host type an error has occurred - “{2}”." + /// + internal static string Runtime_HostTypeMethodInvocationFailed + { + get { return GetString("Runtime_HostTypeMethodInvocationFailed"); } + } + + /// + /// Looks up a localized string similar to "During getting value of '{0}' property of the `{1}` host type an error has occurred - “{2}”." + /// + internal static string Runtime_HostTypePropertyGettingFailed + { + get { return GetString("Runtime_HostTypePropertyGettingFailed"); } + } + + /// + /// Looks up a localized string similar to "During setting value of '{0}' property of the host type `{1}` an error has occurred - “{2}”." + /// + internal static string Runtime_HostTypePropertySettingFailed + { + get { return GetString("Runtime_HostTypePropertySettingFailed"); } + } + + /// + /// Looks up a localized string similar to "Could not retrieve field '{0}' of the host object, because there was an invalid `this` context." + /// + internal static string Runtime_InvalidThisContextForHostObjectField + { + get { return GetString("Runtime_InvalidThisContextForHostObjectField"); } + } + + /// + /// Looks up a localized string similar to "Could not call method '{0}' of the host object, because there was an invalid `this` context." + /// + internal static string Runtime_InvalidThisContextForHostObjectMethod + { + get { return GetString("Runtime_InvalidThisContextForHostObjectMethod"); } + } + + /// + /// Looks up a localized string similar to "Could not retrieve property '{0}' of the host object, because there was an invalid `this` context." + /// + internal static string Runtime_InvalidThisContextForHostObjectProperty + { + get { return GetString("Runtime_InvalidThisContextForHostObjectProperty"); } + } + + /// + /// Looks up a localized string similar to "Could not find suitable constructor or not enough arguments to invoke of constructor of the `{0}`..." + /// + internal static string Runtime_SuitableConstructorOfHostTypeNotFound + { + get { return GetString("Runtime_SuitableConstructorOfHostTypeNotFound"); } + } + + /// + /// Looks up a localized string similar to "Could not find suitable method or not enough arguments to invoke of '{0}' method of the host object." + /// + internal static string Runtime_SuitableMethodOfHostObjectNotFound + { + get { return GetString("Runtime_SuitableMethodOfHostObjectNotFound"); } + } + + /// + /// Looks up a localized string similar to "{0} packages do not support installation under Mono, but you can to install the native assembly..." + /// + internal static string Engine_ManualInstallationUnderMonoRequired + { + get { return GetString("Engine_ManualInstallationUnderMonoRequired"); } + } + + /// + /// Looks up a localized string similar to "You can build the '{0}' assembly for the current processor architecture by using following..." + /// + internal static string Engine_BuildNativeAssemblyForCurrentProcessorArchitecture + { + get { return GetString("Engine_BuildNativeAssemblyForCurrentProcessorArchitecture"); } + } + + /// + /// Looks up a localized string similar to "Maximum stack size must be non-negative." + /// + internal static string Engine_MaxStackSizeMustBeNonNegative + { + get { return GetString("Engine_MaxStackSizeMustBeNonNegative"); } + } + + /// + /// Looks up a localized string similar to "The parameter '{0}' must have a `{1}` type." + /// + internal static string Common_ArgumentHasIncorrectType + { + get { return GetString("Common_ArgumentHasIncorrectType"); } + } + + private static string GetString(string name) + { + string value = ResourceManager.GetString(name, _resourceCulture); -namespace JavaScriptEngineSwitcher.ChakraCore.Resources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Strings { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Strings() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JavaScriptEngineSwitcher.ChakraCore.Resources.Strings", typeof(Strings).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Failed to add the '{0}' directory to the search path used to locate DLLs for the application.. - /// - internal static string Engines_AddingDirectoryToDllSearchPathFailed { - get { - return ResourceManager.GetString("Engines_AddingDirectoryToDllSearchPathFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The '{0}' directory, that should contain `ChakraCore.dll` assembly, does not exist.. - /// - internal static string Engines_ChakraCoreAssemblyDirectoryNotFound { - get { - return ResourceManager.GetString("Engines_ChakraCoreAssemblyDirectoryNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During invocation of the host delegate an error has occurred - “{0}”.. - /// - internal static string Runtime_HostDelegateInvocationFailed { - get { - return ResourceManager.GetString("Runtime_HostDelegateInvocationFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During getting value of '{0}' field of the host object an error has occurred - “{1}”.. - /// - internal static string Runtime_HostObjectFieldGettingFailed { - get { - return ResourceManager.GetString("Runtime_HostObjectFieldGettingFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During setting value of '{0}' field of the host object an error has occurred - “{1}”.. - /// - internal static string Runtime_HostObjectFieldSettingFailed { - get { - return ResourceManager.GetString("Runtime_HostObjectFieldSettingFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During invocation of '{0}' method of the host object an error has occurred - “{1}”.. - /// - internal static string Runtime_HostObjectMethodInvocationFailed { - get { - return ResourceManager.GetString("Runtime_HostObjectMethodInvocationFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During getting value of '{0}' property of the host object an error has occurred - “{1}”.. - /// - internal static string Runtime_HostObjectPropertyGettingFailed { - get { - return ResourceManager.GetString("Runtime_HostObjectPropertyGettingFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During setting value of '{0}' property of the host object an error has occurred - “{1}”.. - /// - internal static string Runtime_HostObjectPropertySettingFailed { - get { - return ResourceManager.GetString("Runtime_HostObjectPropertySettingFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During invocation of constructor of the `{0}` host type an error has occurred - “{1}”.. - /// - internal static string Runtime_HostTypeConstructorInvocationFailed { - get { - return ResourceManager.GetString("Runtime_HostTypeConstructorInvocationFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not create instance of the `{0}` host type, because it does not have any public constructor.. - /// - internal static string Runtime_HostTypeConstructorNotFound { - get { - return ResourceManager.GetString("Runtime_HostTypeConstructorNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During getting value of '{0}' field of the `{1}` host type an error has occurred - “{2}”.. - /// - internal static string Runtime_HostTypeFieldGettingFailed { - get { - return ResourceManager.GetString("Runtime_HostTypeFieldGettingFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During setting value of '{0}' field of the `{1}` host type an error has occurred - “{2}”.. - /// - internal static string Runtime_HostTypeFieldSettingFailed { - get { - return ResourceManager.GetString("Runtime_HostTypeFieldSettingFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During invocation of '{0}' method of the `{1}` host type an error has occurred - “{2}”.. - /// - internal static string Runtime_HostTypeMethodInvocationFailed { - get { - return ResourceManager.GetString("Runtime_HostTypeMethodInvocationFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During getting value of '{0}' property of the `{1}` host type an error has occurred - “{2}”.. - /// - internal static string Runtime_HostTypePropertyGettingFailed { - get { - return ResourceManager.GetString("Runtime_HostTypePropertyGettingFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During setting value of '{0}' property of the host type `{1}` an error has occurred - “{2}”.. - /// - internal static string Runtime_HostTypePropertySettingFailed { - get { - return ResourceManager.GetString("Runtime_HostTypePropertySettingFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not retrieve field '{0}' of the host object, because there was an invalid `this` context.. - /// - internal static string Runtime_InvalidThisContextForHostObjectField { - get { - return ResourceManager.GetString("Runtime_InvalidThisContextForHostObjectField", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not call method '{0}' of the host object, because there was an invalid `this` context.. - /// - internal static string Runtime_InvalidThisContextForHostObjectMethod { - get { - return ResourceManager.GetString("Runtime_InvalidThisContextForHostObjectMethod", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not retrieve property '{0}' of the host object, because there was an invalid `this` context.. - /// - internal static string Runtime_InvalidThisContextForHostObjectProperty { - get { - return ResourceManager.GetString("Runtime_InvalidThisContextForHostObjectProperty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not find suitable constructor or not enough arguments to invoke of constructor of the `{0}` host type.. - /// - internal static string Runtime_SuitableConstructorOfHostTypeNotFound { - get { - return ResourceManager.GetString("Runtime_SuitableConstructorOfHostTypeNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not find suitable method or not enough arguments to invoke of '{0}' method of the host object.. - /// - internal static string Runtime_SuitableMethodOfHostObjectNotFound { - get { - return ResourceManager.GetString("Runtime_SuitableMethodOfHostObjectNotFound", resourceCulture); - } - } - } -} + return value; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.resx b/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.resx index 3a96a33b..9b46bfe7 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.resx +++ b/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.resx @@ -120,9 +120,6 @@ Failed to add the '{0}' directory to the search path used to locate DLLs for the application. - - The '{0}' directory, that should contain `ChakraCore.dll` assembly, does not exist. - During invocation of the host delegate an error has occurred - “{0}”. @@ -177,4 +174,16 @@ Could not find suitable method or not enough arguments to invoke of '{0}' method of the host object. + + {0} packages do not support installation under Mono, but you can to install the native assembly manually ({1}). + + + You can build the '{0}' assembly for the current processor architecture by using following instructions from official repository - {1}. + + + Maximum stack size must be non-negative. + + + The parameter '{0}' must have a `{1}` type. + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.ru-ru.Designer.cs b/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.ru-RU.Designer.cs similarity index 100% rename from src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.ru-ru.Designer.cs rename to src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.ru-RU.Designer.cs diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.ru-ru.resx b/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.ru-RU.resx similarity index 91% rename from src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.ru-ru.resx rename to src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.ru-RU.resx index d8524de4..3acdd3a0 100644 --- a/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.ru-ru.resx +++ b/src/JavaScriptEngineSwitcher.ChakraCore/Resources/Strings.ru-RU.resx @@ -120,9 +120,6 @@ Не удалось добавить "{0}" директорию к пути поиска, используемому для определения местонахождения DLL! - - Директория "{0}", в которой должна находится сборка `ChakraCore.dll`, не существует! - Во время вызова делегата хоста произошла ошибка - «{0}»! @@ -177,4 +174,16 @@ Не получается найти подходящий метод или не хватает аргументов для вызова метода "{0}" объекта хоста! + + {0} пакеты не поддерживают установку под Mono, но можно установить нативную сборку вручную ({1}). + + + Вы можете собрать "{0}" для текущей архитектуры процессора, используя следующие инструкции из официального репозитория - {1}. + + + Максимальный размер стека должен быть неотрицательным! + + + Параметр с именем "{0}" должен иметь тип `{1}`! + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/ScriptDispatcher.cs b/src/JavaScriptEngineSwitcher.ChakraCore/ScriptDispatcher.cs new file mode 100644 index 00000000..fc19a767 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/ScriptDispatcher.cs @@ -0,0 +1,435 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +#if NET45_OR_GREATER || NETSTANDARD +using System.Runtime.ExceptionServices; +#endif +using System.Threading; + +#if NET40 +using JavaScriptEngineSwitcher.Core.Extensions; +#endif +using JavaScriptEngineSwitcher.Core.Utilities; + +namespace JavaScriptEngineSwitcher.ChakraCore +{ + /// + /// Provides services for managing the queue of script tasks on the thread with modified stack size + /// + internal sealed class ScriptDispatcher : IDisposable + { + /// + /// The thread with modified stack size + /// + private Thread _thread; + + /// + /// Event to signal when the new script task entered to the queue + /// + private AutoResetEvent _waitHandle = new AutoResetEvent(false); + + /// + /// Queue of script tasks + /// + private Queue _taskQueue = new Queue(); + + /// + /// Synchronizer of script task queue + /// + private readonly object _taskQueueSynchronizer = new object(); + + /// + /// Flag that object is destroyed + /// + private InterlockedStatedFlag _disposedFlag = new InterlockedStatedFlag(); + + +#if NETSTANDARD1_3 + /// + /// Constructs an instance of script dispatcher + /// + public ScriptDispatcher() + { + _thread = new Thread(StartThread) +#else + /// + /// Constructs an instance of script dispatcher + /// + /// The maximum stack size, in bytes, to be used by the thread, + /// or 0 to use the default maximum stack size specified in the header for the executable. + public ScriptDispatcher(int maxStackSize) + { + _thread = new Thread(StartThread, maxStackSize) +#endif + { + IsBackground = true + }; + _thread.Start(); + } + + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private void VerifyNotDisposed() + { + if (_disposedFlag.IsSet()) + { + throw new ObjectDisposedException(ToString()); + } + } + + /// + /// Starts a thread with modified stack size. + /// Loops forever, processing script tasks from the queue. + /// + private void StartThread() + { + while (true) + { + ScriptTask task = null; + + lock (_taskQueueSynchronizer) + { + if (_taskQueue.Count > 0) + { + task = _taskQueue.Dequeue(); + if (task == null) + { + _taskQueue.Clear(); + return; + } + } + } + + if (task != null) + { + task.Run(); + } + else + { + _waitHandle.WaitOne(); + } + } + } + + /// + /// Adds a script task to the end of the queue + /// + /// Script task + private void EnqueueTask(ScriptTask task) + { + lock (_taskQueueSynchronizer) + { + _taskQueue.Enqueue(task); + } + _waitHandle.Set(); + } + + /// + /// Executes a script task + /// + /// Script task + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private void ExecuteTask(ScriptTask task) + { + EnqueueTask(task); + task.Wait(); + + Exception exception = task.Exception; + if (exception != null) + { +#if NET45_OR_GREATER || NETSTANDARD + ExceptionDispatchInfo.Capture(exception).Throw(); +#elif NET40 + exception.PreserveStackTrace(); + throw exception; +#else +#error No implementation for this target +#endif + } + } + + /// + /// Runs a specified delegate on the thread with modified stack size, + /// and returns its result as an . + /// Blocks until the invocation of delegate is completed. + /// + /// The type of the return value of the method, + /// that specified delegate encapsulates + /// Delegate to invocation + /// Result of the delegate invocation + public T Invoke(Func func) + { + VerifyNotDisposed(); + + if (func == null) + { + throw new ArgumentNullException(nameof(func)); + } + + if (Thread.CurrentThread == _thread) + { + return func(); + } + + using (var task = new ScriptTaskWithResult(func)) + { + ExecuteTask(task); + return task.Result; + } + } + + /// + /// Runs a specified delegate on the thread with modified stack size. + /// Blocks until the invocation of delegate is completed. + /// + /// Delegate to invocation + public void Invoke(Action action) + { + VerifyNotDisposed(); + + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + if (Thread.CurrentThread == _thread) + { + action(); + return; + } + + using (var task = new ScriptTaskWithoutResult(action)) + { + ExecuteTask(task); + } + } + + #region IDisposable implementation + + /// + /// Destroys a script dispatcher + /// + public void Dispose() + { + if (_disposedFlag.Set()) + { + EnqueueTask(null); + + if (_thread != null) + { + _thread.Join(); + _thread = null; + } + + if (_waitHandle != null) + { + _waitHandle.Dispose(); + _waitHandle = null; + } + + _taskQueue = null; + } + } + + #endregion + + #region Internal types + + /// + /// Represents a script task, that must be executed on separate thread + /// + private abstract class ScriptTask : IDisposable + { + /// + /// Event to signal when the invocation of delegate has completed + /// + protected ManualResetEvent _waitHandle = new ManualResetEvent(false); + + /// + /// Exception, that occurred during the invocation of delegate + /// + protected Exception _exception; + + /// + /// Flag that object is destroyed + /// + protected StatedFlag _disposedFlag = new StatedFlag(); + + /// + /// Gets a exception, that occurred during the invocation of delegate. + /// If no exception has occurred, this will be null. + /// + public Exception Exception + { + get { return _exception; } + } + + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + protected void VerifyNotDisposed() + { + if (_disposedFlag.IsSet()) + { + throw new ObjectDisposedException(ToString()); + } + } + + /// + /// Runs a script task + /// + public abstract void Run(); + + /// + /// Waits for the script task to complete execution + /// + public void Wait() + { + VerifyNotDisposed(); + + _waitHandle.WaitOne(); + } + + #region IDisposable implementation + + /// + /// Destroys a script task + /// + public virtual void Dispose() + { + if (_waitHandle != null) + { + _waitHandle.Dispose(); + _waitHandle = null; + } + + _exception = null; + } + + #endregion + } + + /// + /// Represents a script task with result, that must be executed on separate thread + /// + private sealed class ScriptTaskWithResult : ScriptTask + { + /// + /// Delegate to invocation + /// + private Func _func; + + /// + /// Result of the delegate invocation + /// + private TResult _result; + + /// + /// Gets a result of the delegate invocation + /// + public TResult Result + { + get { return _result; } + } + + + /// + /// Constructs an instance of script task with result + /// + /// Delegate to invocation + public ScriptTaskWithResult(Func func) + { + _func = func; + } + + + /// + /// Runs a script task + /// + public override void Run() + { + VerifyNotDisposed(); + + try + { + _result = _func(); + } + catch (Exception e) + { + _exception = e; + } + + _waitHandle.Set(); + } + + /// + /// Destroys a script task + /// + public override void Dispose() + { + if (_disposedFlag.Set()) + { + base.Dispose(); + + _result = default(TResult); + _func = null; + } + } + } + + /// + /// Represents a script task without result, that must be executed on separate thread + /// + private sealed class ScriptTaskWithoutResult : ScriptTask + { + /// + /// Delegate to invocation + /// + private Action _action; + + + /// + /// Constructs an instance of script task without result + /// + /// Delegate to invocation + public ScriptTaskWithoutResult(Action action) + { + _action = action; + } + + + /// + /// Runs a script task + /// + public override void Run() + { + VerifyNotDisposed(); + + try + { + _action(); + } + catch (Exception e) + { + _exception = e; + } + + _waitHandle.Set(); + } + + /// + /// Destroys a script task + /// + public override void Dispose() + { + if (_disposedFlag.Set()) + { + base.Dispose(); + + _action = null; + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.ChakraCore/readme.txt b/src/JavaScriptEngineSwitcher.ChakraCore/readme.txt new file mode 100644 index 00000000..c7484b48 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.ChakraCore/readme.txt @@ -0,0 +1,40 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: ChakraCore v3.27.3 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + JavaScriptEngineSwitcher.ChakraCore contains a `ChakraCoreJsEngine` adapter + (wrapper for the ChakraCore (https://github.com/chakra-core/ChakraCore)). + Project was based on the code of Chakra-Samples + (https://github.com/Microsoft/Chakra-Samples) and jsrt-dotnet + (https://github.com/robpaveza/jsrt-dotnet). + + This package does not contain the native implementations of ChakraCore. + Therefore, you need to choose and install the most appropriate package(s) for + your platform. The following packages are available: + + * JavaScriptEngineSwitcher.ChakraCore.Native.win-x86 + * JavaScriptEngineSwitcher.ChakraCore.Native.win-x64 + * JavaScriptEngineSwitcher.ChakraCore.Native.win-arm + * JavaScriptEngineSwitcher.ChakraCore.Native.win-arm64 + * JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 + * JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64 + + ============= + RELEASE NOTES + ============= + ChakraCore was updated to version of August 1, 2024. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Configuration/CoreConfiguration.cs b/src/JavaScriptEngineSwitcher.Core/Configuration/CoreConfiguration.cs deleted file mode 100644 index 1029b489..00000000 --- a/src/JavaScriptEngineSwitcher.Core/Configuration/CoreConfiguration.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace JavaScriptEngineSwitcher.Core.Configuration -{ - using System.Configuration; - - /// - /// Configuration settings of core - /// - public sealed class CoreConfiguration : ConfigurationSection - { - /// - /// Gets or sets a name of default JavaScript engine - /// - [ConfigurationProperty("defaultEngine", DefaultValue = "")] - public string DefaultEngine - { - get { return (string)this["defaultEngine"]; } - set { this["defaultEngine"] = value; } - } - - /// - /// Gets a list of registered JavaScript engines - /// - [ConfigurationProperty("engines", IsRequired = true)] - public JsEngineRegistrationList Engines - { - get { return (JsEngineRegistrationList)this["engines"]; } - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Configuration/JsEngineRegistration.cs b/src/JavaScriptEngineSwitcher.Core/Configuration/JsEngineRegistration.cs deleted file mode 100644 index 7075abb1..00000000 --- a/src/JavaScriptEngineSwitcher.Core/Configuration/JsEngineRegistration.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace JavaScriptEngineSwitcher.Core.Configuration -{ - using System.Configuration; - - /// - /// JavaScript engine registration - /// - public sealed class JsEngineRegistration : ConfigurationElement - { - /// - /// Gets or sets a JavaScript engine name - /// - [ConfigurationProperty("name", IsKey = true, IsRequired = true)] - public string Name - { - get { return (string)this["name"]; } - set { this["name"] = value; } - } - - /// - /// Gets or sets a JavaScript engine .NET-type name - /// - [ConfigurationProperty("type", IsRequired = true)] - public string Type - { - get { return (string)this["type"]; } - set { this["type"] = value; } - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Configuration/JsEngineRegistrationList.cs b/src/JavaScriptEngineSwitcher.Core/Configuration/JsEngineRegistrationList.cs deleted file mode 100644 index faba32ad..00000000 --- a/src/JavaScriptEngineSwitcher.Core/Configuration/JsEngineRegistrationList.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace JavaScriptEngineSwitcher.Core.Configuration -{ - using System.Configuration; - - /// - /// List of registered JavaScript engines - /// - public sealed class JsEngineRegistrationList : ConfigurationElementCollection - { - /// - /// Creates a new JavaScript engine registration - /// - /// JavaScript engine registration - protected override ConfigurationElement CreateNewElement() - { - return new JsEngineRegistration(); - } - - /// - /// Gets a key of the specified JavaScript engine registration - /// - /// JavaScript engine registration - /// Key - protected override object GetElementKey(ConfigurationElement element) - { - return ((JsEngineRegistration)element).Name; - } - - /// - /// Gets a JavaScript engine registration by JavaScript engine name - /// - /// JavaScript engine name - /// JavaScript engine registration - public new JsEngineRegistration this[string name] - { - get { return (JsEngineRegistration)BaseGet(name); } - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Constants/JsErrorCategory.cs b/src/JavaScriptEngineSwitcher.Core/Constants/JsErrorCategory.cs new file mode 100644 index 00000000..ffcde169 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/Constants/JsErrorCategory.cs @@ -0,0 +1,15 @@ +namespace JavaScriptEngineSwitcher.Core.Constants +{ + internal static class JsErrorCategory + { + public const string Unknown = "Unknown error"; + public const string Compilation = "Compilation error"; + public const string Runtime = "Runtime error"; + public const string Interrupted = "Interrupted error"; + public const string Timeout = "Timeout error"; + public const string Usage = "Usage error"; + public const string Engine = "Engine error"; + public const string EngineLoad = "Engine load error"; + public const string Fatal = "Fatal error"; + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Constants/JsErrorType.cs b/src/JavaScriptEngineSwitcher.Core/Constants/JsErrorType.cs new file mode 100644 index 00000000..b7f16755 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/Constants/JsErrorType.cs @@ -0,0 +1,15 @@ +namespace JavaScriptEngineSwitcher.Core.Constants +{ + public static class JsErrorType + { + public const string Common = "Error"; + public const string Eval = "EvalError"; + public const string Internal = "InternalError"; + public const string Range = "RangeError"; + public const string Reference = "ReferenceError"; + public const string RegExp = "RegExpError"; + public const string Syntax = "SyntaxError"; + public const string Type = "TypeError"; + public const string URI = "URIError"; + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/EmptyValueException.cs b/src/JavaScriptEngineSwitcher.Core/EmptyValueException.cs deleted file mode 100644 index 0e7abf79..00000000 --- a/src/JavaScriptEngineSwitcher.Core/EmptyValueException.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace JavaScriptEngineSwitcher.Core -{ - using System; - - /// - /// The exception that is thrown when a specified value is null or empty - /// - public sealed class EmptyValueException : Exception - { - /// - /// Initializes a new instance of the JavaScriptEngineSwitcher.Core.EmptyValueException class - /// with a specified error message - /// - /// The message that describes the error - public EmptyValueException(string message) - : base(message) - { } - - /// - /// Initializes a new instance of the JavaScriptEngineSwitcher.Core.EmptyValueException class - /// with a specified error message and a reference to the inner exception that is the cause of this exception - /// - /// The error message that explains the reason for the exception - /// The exception that is the cause of the current exception - public EmptyValueException(string message, Exception innerException) - : base(message, innerException) - { } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Extensions/ExceptionExtensions.cs b/src/JavaScriptEngineSwitcher.Core/Extensions/ExceptionExtensions.cs new file mode 100644 index 00000000..dd956c8f --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/Extensions/ExceptionExtensions.cs @@ -0,0 +1,29 @@ +#if NET40 +using System; +using System.Reflection; + +namespace JavaScriptEngineSwitcher.Core.Extensions +{ + /// + /// Exception extensions + /// + public static class ExceptionExtensions + { + /// + /// Preserves a stack trace of exception + /// + /// The exception + public static void PreserveStackTrace(this Exception source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + MethodInfo preserveStackTraceMethodInfo = typeof(Exception).GetMethod("InternalPreserveStackTrace", + BindingFlags.Instance | BindingFlags.NonPublic); + preserveStackTraceMethodInfo.Invoke(source, null); + } + } +} +#endif \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Extensions/StringExtensions.cs b/src/JavaScriptEngineSwitcher.Core/Extensions/StringExtensions.cs new file mode 100644 index 00000000..747d67c5 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/Extensions/StringExtensions.cs @@ -0,0 +1,117 @@ +using System; + +namespace JavaScriptEngineSwitcher.Core.Extensions +{ + /// + /// Extensions for String + /// + public static class StringExtensions + { + /// + /// Returns a value indicating whether the specified quoted string occurs within this string + /// + /// Instance of + /// The string without quotes to seek + /// true if the quoted value occurs within this string; otherwise, false + public static bool ContainsQuotedValue(this string source, string value) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + bool result = source.Contains("'" + value + "'") || source.Contains("\"" + value + "\""); + + return result; + } + + /// + /// Removes leading occurrence of the specified string from the current object + /// + /// Instance of + /// An string to remove + /// The string that remains after removing of the specified string from the start of + /// the current string + public static string TrimStart(this string source, string trimString) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (trimString == null) + { + throw new ArgumentNullException(nameof(trimString)); + } + + if (source.Length == 0 || trimString.Length == 0) + { + return source; + } + + string result = source; + if (source.StartsWith(trimString, StringComparison.Ordinal)) + { + result = source.Substring(trimString.Length); + } + + return result; + } + + /// + /// Splits a string into lines + /// + /// Instance of + /// An array of lines + public static string[] SplitToLines(this string source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + string[] result = source.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); + + return result; + } + + /// + /// Gets a character at the specified index from the string. + /// A return value indicates whether the receiving succeeded. + /// + /// The source string + /// The zero-based index of the character + /// When this method returns, contains the character from the string, + /// if the receiving succeeded, or null character if the receiving failed. + /// The receiving fails if the index out of bounds. + /// true if the character was received successfully; otherwise, false + internal static bool TryGetChar(this string source, int index, out char result) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + bool isSuccess; + int length = source.Length; + + if (length > 0 && index >= 0 && index < length) + { + result = source[index]; + isSuccess = true; + } + else + { + result = '\0'; + isSuccess = false; + } + + return isSuccess; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Extensions/TypeExtensions.cs b/src/JavaScriptEngineSwitcher.Core/Extensions/TypeExtensions.cs new file mode 100644 index 00000000..a7229171 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/Extensions/TypeExtensions.cs @@ -0,0 +1,102 @@ +using System; +#if NETSTANDARD1_3 +using System.Reflection; +#endif + +namespace JavaScriptEngineSwitcher.Core.Extensions +{ + /// + /// Type extensions + /// + public static class TypeExtensions + { + /// + /// Gets a underlying type code of the specified + /// + /// The type whose underlying type code to get + /// The code of the underlying type + public static TypeCode GetTypeCode(this Type source) + { + TypeCode typeCode; + +#if NETSTANDARD1_3 + if (source == null) + { + typeCode = TypeCode.Empty; + } + else if (source == typeof(bool)) + { + typeCode = TypeCode.Boolean; + } + else if (source == typeof(char)) + { + typeCode = TypeCode.Char; + } + else if (source == typeof(sbyte)) + { + typeCode = TypeCode.SByte; + } + else if (source == typeof(byte)) + { + typeCode = TypeCode.Byte; + } + else if (source == typeof(short)) + { + typeCode = TypeCode.Int16; + } + else if (source == typeof(ushort)) + { + typeCode = TypeCode.UInt16; + } + else if (source == typeof(int)) + { + typeCode = TypeCode.Int32; + } + else if (source == typeof(uint)) + { + typeCode = TypeCode.UInt32; + } + else if (source == typeof(long)) + { + typeCode = TypeCode.Int64; + } + else if (source == typeof(ulong)) + { + typeCode = TypeCode.UInt64; + } + else if (source == typeof(float)) + { + typeCode = TypeCode.Single; + } + else if (source == typeof(double)) + { + typeCode = TypeCode.Double; + } + else if (source == typeof(decimal)) + { + typeCode = TypeCode.Decimal; + } + else if (source == typeof(DateTime)) + { + typeCode = TypeCode.DateTime; + } + else if (source == typeof(string)) + { + typeCode = TypeCode.String; + } + else if (source.GetTypeInfo().IsEnum) + { + typeCode = GetTypeCode(Enum.GetUnderlyingType(source)); + } + else + { + typeCode = TypeCode.Object; + } +#else + typeCode = Type.GetTypeCode(source); +#endif + + return typeCode; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Helpers/CommonRegExps.cs b/src/JavaScriptEngineSwitcher.Core/Helpers/CommonRegExps.cs new file mode 100644 index 00000000..379e9f8b --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/Helpers/CommonRegExps.cs @@ -0,0 +1,24 @@ +namespace JavaScriptEngineSwitcher.Core.Helpers +{ + /// + /// Common regular expressions + /// + public static class CommonRegExps + { + /// + /// Pattern for working with JS names + /// + public static readonly string JsNamePattern = @"[$_\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}]" + + @"[$_\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}\u200C\u200D\p{Mn}\p{Mc}\p{Nd}\p{Pc}]*"; + + /// + /// Pattern for working with JS full names + /// + public static readonly string JsFullNamePattern = JsNamePattern + @"(?:\." + JsNamePattern + @")*"; + + /// + /// Pattern for working with document names + /// + public static readonly string DocumentNamePattern = @"[^\s*?""<>|][^\t\n\r*?""<>|]*?"; + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Helpers/ErrorLocationItem.cs b/src/JavaScriptEngineSwitcher.Core/Helpers/ErrorLocationItem.cs new file mode 100644 index 00000000..018047d7 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/Helpers/ErrorLocationItem.cs @@ -0,0 +1,66 @@ +namespace JavaScriptEngineSwitcher.Core.Helpers +{ + /// + /// Script error location item + /// + public sealed class ErrorLocationItem + { + /// + /// Gets or sets a function name + /// + public string FunctionName + { + get; + set; + } + + /// + /// Gets or sets a document name + /// + public string DocumentName + { + get; + set; + } + + /// + /// Gets or sets a line number + /// + public int LineNumber + { + get; + set; + } + + /// + /// Gets or sets a column number + /// + public int ColumnNumber + { + get; + set; + } + + /// + /// Gets or sets a source fragment + /// + public string SourceFragment + { + get; + set; + } + + + /// + /// Constructs an instance of the script error location item + /// + public ErrorLocationItem() + { + FunctionName = string.Empty; + DocumentName = string.Empty; + LineNumber = 0; + ColumnNumber = 0; + SourceFragment = string.Empty; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Helpers/JsErrorHelpers.cs b/src/JavaScriptEngineSwitcher.Core/Helpers/JsErrorHelpers.cs new file mode 100644 index 00000000..d7d7969a --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/Helpers/JsErrorHelpers.cs @@ -0,0 +1,579 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +using AdvancedStringBuilder; + +using JavaScriptEngineSwitcher.Core.Extensions; +using JavaScriptEngineSwitcher.Core.Resources; + +namespace JavaScriptEngineSwitcher.Core.Helpers +{ + /// + /// JS error helpers + /// + public static class JsErrorHelpers + { + #region Error location + + /// + /// Pattern for working with document names with coordinates + /// + private static readonly string DocumentNameWithCoordinatesPattern = + @"(?" + CommonRegExps.DocumentNamePattern + @"):" + + @"(?\d+)(?::(?\d+))?"; + + /// + /// Regular expression for working with line of the script error location + /// + private static readonly Regex _errorLocationLineRegex = + new Regex(@"^[ ]{3,4}at " + + @"(?:" + + @"(?[\w][\w ]*|" + CommonRegExps.JsFullNamePattern + @") " + + @"\(" + DocumentNameWithCoordinatesPattern + @"\)" + + @"|" + + DocumentNameWithCoordinatesPattern + + @")" + + @"(?: -> (?[^\n\r]+))?$"); + + /// + /// Parses a string representation of the script error location to produce an array of + /// instances + /// + /// String representation of the script error location + /// An array of instances + public static ErrorLocationItem[] ParseErrorLocation(string errorLocation) + { + if (string.IsNullOrWhiteSpace(errorLocation)) + { + return new ErrorLocationItem[0]; + } + + var errorLocationItems = new List(); + string[] lines = errorLocation.SplitToLines(); + int lineCount = lines.Length; + + for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) + { + string line = lines[lineIndex]; + Match lineMatch = _errorLocationLineRegex.Match(line); + + if (lineMatch.Success) + { + GroupCollection lineGroups = lineMatch.Groups; + + var errorLocationItem = new ErrorLocationItem + { + FunctionName = lineGroups["functionName"].Value, + DocumentName = lineGroups["documentName"].Value, + LineNumber = int.Parse(lineGroups["lineNumber"].Value), + ColumnNumber = lineGroups["columnNumber"].Success ? + int.Parse(lineGroups["columnNumber"].Value) : 0, + SourceFragment = lineGroups["sourceFragment"].Value + }; + errorLocationItems.Add(errorLocationItem); + } + else + { + Debug.WriteLine(string.Format(Strings.Runtime_InvalidErrorLocationLineFormat, line)); + return new ErrorLocationItem[0]; + } + } + + return errorLocationItems.ToArray(); + } + + /// + /// Produces a string representation of the script error location from array of + /// instances + /// + /// An array of instances + /// Flag for whether to omit source fragment + /// String representation of the script error location + public static string StringifyErrorLocationItems(ErrorLocationItem[] errorLocationItems, + bool omitSourceFragment = false) + { + if (errorLocationItems == null) + { + throw new ArgumentException(nameof(errorLocationItems)); + } + + int locationItemCount = errorLocationItems.Length; + if (locationItemCount == 0) + { + return string.Empty; + } + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder locationBuilder = stringBuilderPool.Rent(); + + for (int locationItemIndex = 0; locationItemIndex < locationItemCount; locationItemIndex++) + { + ErrorLocationItem locationItem = errorLocationItems[locationItemIndex]; + + if (locationItemIndex > 0) + { + locationBuilder.AppendLine(); + } + WriteErrorLocationLine(locationBuilder, locationItem.FunctionName, locationItem.DocumentName, + locationItem.LineNumber, locationItem.ColumnNumber, + !omitSourceFragment ? locationItem.SourceFragment : string.Empty); + } + + string errorLocation = locationBuilder.ToString(); + stringBuilderPool.Return(locationBuilder); + + return errorLocation; + } + + /// + /// Writes a error location line to the buffer + /// + /// Instance of + /// Function name + /// Document name + /// Line number + /// Column number + /// Source fragment + public static void WriteErrorLocationLine(StringBuilder buffer, string functionName, + string documentName, int lineNumber, int columnNumber, string sourceFragment = "") + { + bool functionNameNotEmpty = !string.IsNullOrWhiteSpace(functionName); + bool documentNameNotEmpty = !string.IsNullOrWhiteSpace(documentName); + + if (functionNameNotEmpty || documentNameNotEmpty || lineNumber > 0) + { + buffer.Append(" at "); + if (functionNameNotEmpty) + { + buffer.Append(functionName); + } + if (documentNameNotEmpty || lineNumber > 0) + { + if (functionNameNotEmpty) + { + buffer.Append(" ("); + } + if (documentNameNotEmpty) + { + buffer.Append(documentName); + } + if (lineNumber > 0) + { + if (documentNameNotEmpty) + { + buffer.Append(":"); + } + buffer.Append(lineNumber); + if (columnNumber > 0) + { + buffer.Append(":"); + buffer.Append(columnNumber); + } + } + if (functionNameNotEmpty) + { + buffer.Append(")"); + } + if (!string.IsNullOrWhiteSpace(sourceFragment)) + { + buffer.Append(" -> "); + buffer.Append(sourceFragment); + } + } + } + } + + #endregion + + #region Generation of error messages + + /// + /// Generates a engine load error message + /// + /// Description of error + /// Name of JS engine + /// Makes a quote from the description + /// Engine load error message + public static string GenerateEngineLoadErrorMessage(string description, string engineName, + bool quoteDescription = false) + { + if (engineName == null) + { + throw new ArgumentNullException(nameof(engineName)); + } + + if (string.IsNullOrWhiteSpace(engineName)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(engineName)), + nameof(engineName) + ); + } + + string jsEngineNotLoadedPart = string.Format(Strings.Engine_JsEngineNotLoaded, engineName); + string message; + + if (!string.IsNullOrWhiteSpace(description)) + { + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder messageBuilder = stringBuilderPool.Rent(); + messageBuilder.Append(jsEngineNotLoadedPart); + messageBuilder.Append(" "); + if (quoteDescription) + { + messageBuilder.AppendFormat(Strings.Common_SeeOriginalErrorMessage, description); + } + else + { + messageBuilder.Append(description); + } + + message = messageBuilder.ToString(); + stringBuilderPool.Return(messageBuilder); + } + else + { + message = jsEngineNotLoadedPart; + } + + return message; + } + + /// + /// Generates a script error message + /// + /// Type of the script error + /// Description of error + /// Document name + /// Line number + /// Column number + /// Source fragment + /// Script error message + public static string GenerateScriptErrorMessage(string type, string description, + string documentName, int lineNumber, int columnNumber, string sourceFragment = "") + { + return GenerateScriptErrorMessage(type, description, documentName, lineNumber, columnNumber, + sourceFragment, string.Empty); + } + + /// + /// Generates a script error message + /// + /// Type of the script error + /// Description of error + /// String representation of the script call stack + /// Script error message + public static string GenerateScriptErrorMessage(string type, string description, string callStack) + { + return GenerateScriptErrorMessage(type, description, string.Empty, 0, 0, string.Empty, callStack); + } + + /// + /// Generates a script error message + /// + /// Type of the script error + /// Description of error + /// Document name + /// Line number + /// Column number + /// Source fragment + /// String representation of the script call stack + /// Script error message + private static string GenerateScriptErrorMessage(string type, string description, string documentName, + int lineNumber, int columnNumber, string sourceFragment, string callStack) + { + if (description == null) + { + throw new ArgumentNullException(nameof(description)); + } + + if (string.IsNullOrWhiteSpace(description)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(description)), + nameof(description) + ); + } + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder messageBuilder = stringBuilderPool.Rent(); + if (!string.IsNullOrWhiteSpace(type)) + { + messageBuilder.Append(type); + messageBuilder.Append(": "); + } + messageBuilder.Append(description); + + if (!string.IsNullOrWhiteSpace(callStack)) + { + messageBuilder.AppendLine(); + messageBuilder.Append(callStack); + } + else + { + if (!string.IsNullOrWhiteSpace(documentName) || lineNumber > 0) + { + messageBuilder.AppendLine(); + WriteErrorLocationLine(messageBuilder, string.Empty, documentName, lineNumber, columnNumber, + sourceFragment); + } + } + + string errorMessage = messageBuilder.ToString(); + stringBuilderPool.Return(messageBuilder); + + return errorMessage; + } + + #endregion + + #region Generation of error details + + /// + /// Generates a detailed error message + /// + /// JS exception + /// Flag for whether to omit message + /// Detailed error message + public static string GenerateErrorDetails(JsException jsException, bool omitMessage = false) + { + if (jsException == null) + { + throw new ArgumentNullException(nameof(jsException)); + } + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder detailsBuilder = stringBuilderPool.Rent(); + WriteCommonErrorDetails(detailsBuilder, jsException, omitMessage); + + var jsScriptException = jsException as JsScriptException; + if (jsScriptException != null) + { + WriteScriptErrorDetails(detailsBuilder, jsScriptException); + + var jsRuntimeException = jsScriptException as JsRuntimeException; + if (jsRuntimeException != null) + { + WriteRuntimeErrorDetails(detailsBuilder, jsRuntimeException); + } + } + + detailsBuilder.TrimEnd(); + + string errorDetails = detailsBuilder.ToString(); + stringBuilderPool.Return(detailsBuilder); + + return errorDetails; + } + + /// + /// Generates a detailed error message + /// + /// JS script exception + /// Flag for whether to omit message + /// Detailed error message + public static string GenerateErrorDetails(JsScriptException jsScriptException, + bool omitMessage = false) + { + if (jsScriptException == null) + { + throw new ArgumentNullException(nameof(jsScriptException)); + } + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder detailsBuilder = stringBuilderPool.Rent(); + WriteCommonErrorDetails(detailsBuilder, jsScriptException, omitMessage); + WriteScriptErrorDetails(detailsBuilder, jsScriptException); + + var jsRuntimeException = jsScriptException as JsRuntimeException; + if (jsRuntimeException != null) + { + WriteRuntimeErrorDetails(detailsBuilder, jsRuntimeException); + } + + detailsBuilder.TrimEnd(); + + string errorDetails = detailsBuilder.ToString(); + stringBuilderPool.Return(detailsBuilder); + + return errorDetails; + } + + /// + /// Generates a detailed error message + /// + /// JS runtime exception + /// Flag for whether to omit message + /// Detailed error message + public static string GenerateErrorDetails(JsRuntimeException jsRuntimeException, + bool omitMessage = false) + { + if (jsRuntimeException == null) + { + throw new ArgumentNullException(nameof(jsRuntimeException)); + } + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder detailsBuilder = stringBuilderPool.Rent(); + WriteCommonErrorDetails(detailsBuilder, jsRuntimeException, omitMessage); + WriteScriptErrorDetails(detailsBuilder, jsRuntimeException); + WriteRuntimeErrorDetails(detailsBuilder, jsRuntimeException); + + detailsBuilder.TrimEnd(); + + string errorDetails = detailsBuilder.ToString(); + stringBuilderPool.Return(detailsBuilder); + + return errorDetails; + } + + /// + /// Writes a detailed error message to the buffer + /// + /// Instance of + /// JS exception + /// Flag for whether to omit message + private static void WriteCommonErrorDetails(StringBuilder buffer, JsException jsException, + bool omitMessage = false) + { + if (!omitMessage) + { + buffer.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_Message, + jsException.Message); + } + if (!string.IsNullOrWhiteSpace(jsException.EngineName)) + { + buffer.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_EngineName, + jsException.EngineName); + } + if (!string.IsNullOrWhiteSpace(jsException.EngineVersion)) + { + buffer.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_EngineVersion, + jsException.EngineVersion); + } + if (!string.IsNullOrWhiteSpace(jsException.Category)) + { + buffer.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_Category, + jsException.Category); + } + if (!string.IsNullOrWhiteSpace(jsException.Description)) + { + buffer.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_Description, + jsException.Description); + } + } + + /// + /// Writes a detailed error message to the buffer + /// + /// Instance of + /// JS script exception + private static void WriteScriptErrorDetails(StringBuilder buffer, + JsScriptException jsScriptException) + { + if (!string.IsNullOrWhiteSpace(jsScriptException.Type)) + { + buffer.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_Type, + jsScriptException.Type); + } + if (!string.IsNullOrWhiteSpace(jsScriptException.DocumentName)) + { + buffer.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_DocumentName, + jsScriptException.DocumentName); + } + if (jsScriptException.LineNumber > 0) + { + buffer.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_LineNumber, + jsScriptException.LineNumber.ToString(CultureInfo.InvariantCulture)); + } + if (jsScriptException.ColumnNumber > 0) + { + buffer.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_ColumnNumber, + jsScriptException.ColumnNumber.ToString(CultureInfo.InvariantCulture)); + } + if (!string.IsNullOrWhiteSpace(jsScriptException.SourceFragment)) + { + buffer.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_SourceFragment, + jsScriptException.SourceFragment); + } + } + + /// + /// Writes a detailed error message to the buffer + /// + /// Instance of + /// JS runtime exception + private static void WriteRuntimeErrorDetails(StringBuilder buffer, + JsRuntimeException jsRuntimeException) + { + if (!string.IsNullOrWhiteSpace(jsRuntimeException.CallStack)) + { + buffer.AppendFormatLine("{1}:{0}{2}", Environment.NewLine, + Strings.ErrorDetails_CallStack, + jsRuntimeException.CallStack); + } + } + + #endregion + + #region Exception wrapping + + public static JsEngineLoadException WrapEngineLoadException(Exception exception, + string engineName, string engineVersion, bool quoteDescription = false) + { + string description = exception.Message; + string message = GenerateEngineLoadErrorMessage(description, engineName, quoteDescription); + + var jsEngineLoadException = new JsEngineLoadException(message, engineName, engineVersion, + exception) + { + Description = description + }; + + return jsEngineLoadException; + } + + #endregion + + #region Obsolete methods + + /// + /// Generates a detailed error message + /// + /// JS exception + /// Detailed error message + [Obsolete("Use a `GenerateErrorDetails` method")] + public static string Format(JsException jsException) + { + return GenerateErrorDetails(jsException); + } + + /// + /// Generates a detailed error message + /// + /// JS script exception + /// Detailed error message + [Obsolete("Use a `GenerateErrorDetails` method")] + public static string Format(JsScriptException jsScriptException) + { + return GenerateErrorDetails(jsScriptException); + } + + /// + /// Generates a detailed error message + /// + /// JS runtime exception + /// Detailed error message + [Obsolete("Use a `GenerateErrorDetails` method")] + public static string Format(JsRuntimeException jsRuntimeException) + { + return GenerateErrorDetails(jsRuntimeException); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Helpers/JsRuntimeErrorHelpers.cs b/src/JavaScriptEngineSwitcher.Core/Helpers/JsRuntimeErrorHelpers.cs deleted file mode 100644 index 04d16027..00000000 --- a/src/JavaScriptEngineSwitcher.Core/Helpers/JsRuntimeErrorHelpers.cs +++ /dev/null @@ -1,89 +0,0 @@ -namespace JavaScriptEngineSwitcher.Core.Helpers -{ - using System; - using System.Globalization; - using System.Text; - - using Resources; - using Utilities; - - /// - /// JavaScript error helpers - /// - public static class JsRuntimeErrorHelpers - { - /// - /// Generates a detailed error message - /// - /// JavaScript engine load exception - /// Detailed error message - public static string Format(JsEngineLoadException jsEngineLoadException) - { - var errorMessage = new StringBuilder(); - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_Message, - jsEngineLoadException.Message); - if (!string.IsNullOrWhiteSpace(jsEngineLoadException.EngineName)) - { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_EngineName, - jsEngineLoadException.EngineName); - } - if (!string.IsNullOrWhiteSpace(jsEngineLoadException.EngineVersion)) - { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_EngineVersion, - jsEngineLoadException.EngineVersion); - } - - return errorMessage.ToString(); - } - - /// - /// Generates a detailed error message - /// - /// JavaScript runtime exception - /// Detailed error message - public static string Format(JsRuntimeException jsRuntimeException) - { - var errorMessage = new StringBuilder(); - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_Message, - jsRuntimeException.Message); - if (!string.IsNullOrWhiteSpace(jsRuntimeException.EngineName)) - { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_EngineName, - jsRuntimeException.EngineName); - } - if (!string.IsNullOrWhiteSpace(jsRuntimeException.EngineVersion)) - { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_EngineVersion, - jsRuntimeException.EngineVersion); - } - if (!string.IsNullOrWhiteSpace(jsRuntimeException.ErrorCode)) - { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_ErrorCode, - jsRuntimeException.ErrorCode); - } - if (!string.IsNullOrWhiteSpace(jsRuntimeException.Category)) - { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_Category, - jsRuntimeException.Category); - } - if (jsRuntimeException.LineNumber > 0) - { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_LineNumber, - jsRuntimeException.LineNumber.ToString(CultureInfo.InvariantCulture)); - } - if (jsRuntimeException.ColumnNumber > 0) - { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_ColumnNumber, - jsRuntimeException.ColumnNumber.ToString(CultureInfo.InvariantCulture)); - } - if (!string.IsNullOrWhiteSpace(jsRuntimeException.SourceFragment)) - { - errorMessage.AppendFormatLine("{1}:{0}{0}{2}", Environment.NewLine, - Strings.ErrorDetails_SourceFragment, - jsRuntimeException.SourceFragment); - } - - return errorMessage.ToString(); - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Helpers/TextHelpers.cs b/src/JavaScriptEngineSwitcher.Core/Helpers/TextHelpers.cs new file mode 100644 index 00000000..aaf9c423 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/Helpers/TextHelpers.cs @@ -0,0 +1,225 @@ +using System; +using System.Text; + +using AdvancedStringBuilder; + +using JavaScriptEngineSwitcher.Core.Extensions; + +namespace JavaScriptEngineSwitcher.Core.Helpers +{ + /// + /// Text helpers + /// + public static class TextHelpers + { + /// + /// Array of characters used to find the next line break + /// + private static readonly char[] _nextLineBreakChars = new char[] { '\r', '\n' }; + + + /// + /// Gets a fragment from the source text + /// + /// Source text + /// Line number + /// Column number + /// Maximum length of the text fragment + public static string GetTextFragment(string sourceText, int lineNumber, int columnNumber, + int maxFragmentLength = 100) + { + if (lineNumber <= 0 || string.IsNullOrEmpty(sourceText)) + { + return string.Empty; + } + + int lineStartPosition; + int lineLength; + GetPositionOfLine(sourceText, lineNumber, out lineStartPosition, out lineLength); + + string fragment = GetTextFragmentInternal(sourceText, lineStartPosition, lineLength, columnNumber, + maxFragmentLength); + + return fragment; + } + + /// + /// Gets a fragment from the text line + /// + /// Content of the text line + /// Column number + /// Maximum length of the text fragment + public static string GetTextFragmentFromLine(string textLine, int columnNumber, + int maxFragmentLength = 100) + { + if (string.IsNullOrEmpty(textLine)) + { + return string.Empty; + } + + int lineStartPosition = 0; + int lineLength = textLine.Length; + string fragment = GetTextFragmentInternal(textLine, lineStartPosition, lineLength, + columnNumber, maxFragmentLength); + + return fragment; + } + + private static string GetTextFragmentInternal(string source, int position, int length, + int columnNumber, int maxFragmentLength) + { + if (length == 0) + { + return string.Empty; + } + + string fragment; + + if (length > maxFragmentLength) + { + const string ellipsisSymbol = "…"; + string startPart = string.Empty; + string endPart = string.Empty; + + var leftOffset = (int)Math.Floor((double)maxFragmentLength / 2); + int fragmentStartPosition = columnNumber - leftOffset - 1; + if (fragmentStartPosition > position) + { + if (length - fragmentStartPosition < maxFragmentLength) + { + fragmentStartPosition = length - maxFragmentLength; + } + } + else + { + fragmentStartPosition = position; + } + int fragmentLength = maxFragmentLength; + + if (fragmentStartPosition > position) + { + startPart = ellipsisSymbol; + } + if (fragmentStartPosition + fragmentLength < length) + { + endPart = ellipsisSymbol; + } + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder fragmentBuilder = stringBuilderPool.Rent(); + if (startPart.Length > 0) + { + fragmentBuilder.Append(startPart); + } + fragmentBuilder.Append(source.Substring(fragmentStartPosition, fragmentLength)); + if (endPart.Length > 0) + { + fragmentBuilder.Append(endPart); + } + + fragment = fragmentBuilder.ToString(); + stringBuilderPool.Return(fragmentBuilder); + } + else + { + fragment = position > 0 || length < source.Length ? + source.Substring(position, length) : source; + } + + return fragment; + } + + private static void GetPositionOfLine(string sourceCode, int lineNumber, out int position, out int length) + { + int currentLineNumber = 0; + position = 0; + length = 0; + + int sourceCodeLength = sourceCode.Length; + if (sourceCodeLength > 0) + { + int currentPosition; + int currentLength; + int sourceCodeEndPosition = sourceCodeLength - 1; + int lineBreakPosition = int.MinValue; + int lineBreakLength = 0; + + do + { + currentLineNumber++; + currentPosition = lineBreakPosition == int.MinValue ? 0 : lineBreakPosition + lineBreakLength; + currentLength = sourceCodeEndPosition - currentPosition + 1; + + FindNextLineBreak(sourceCode, currentPosition, currentLength, + out lineBreakPosition, out lineBreakLength); + + if (currentLineNumber == lineNumber) + { + if (lineBreakPosition != 0) + { + position = currentPosition; + int endPosition = lineBreakPosition != -1 ? + lineBreakPosition - 1 : sourceCodeEndPosition; + length = endPosition - position + 1; + } + break; + } + } + while (lineBreakPosition != -1 && lineBreakPosition <= sourceCodeEndPosition); + } + } + + /// + /// Finds a next line break + /// + /// Source text + /// Position in the input string that defines the leftmost + /// position to be searched + /// Position of line break + /// Length of line break + private static void FindLineBreak(string sourceText, int startPosition, + out int lineBreakPosition, out int lineBreakLength) + { + int length = sourceText.Length - startPosition; + + FindNextLineBreak(sourceText, startPosition, length, + out lineBreakPosition, out lineBreakLength); + } + + /// + /// Finds a next line break + /// + /// Source text + /// Position in the input string that defines the leftmost + /// position to be searched + /// Number of characters in the substring to include in the search + /// Position of line break + /// Length of line break + public static void FindNextLineBreak(string sourceText, int startPosition, int length, + out int lineBreakPosition, out int lineBreakLength) + { + lineBreakPosition = sourceText.IndexOfAny(_nextLineBreakChars, startPosition, length); + if (lineBreakPosition != -1) + { + lineBreakLength = 1; + char currentCharacter = sourceText[lineBreakPosition]; + + if (currentCharacter == '\r') + { + int nextCharacterPosition = lineBreakPosition + 1; + char nextCharacter; + + if (sourceText.TryGetChar(nextCharacterPosition, out nextCharacter) + && nextCharacter == '\n') + { + lineBreakLength = 2; + } + } + } + else + { + lineBreakLength = 0; + } + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Helpers/ValidationHelpers.cs b/src/JavaScriptEngineSwitcher.Core/Helpers/ValidationHelpers.cs index b88644f9..cadeb669 100644 --- a/src/JavaScriptEngineSwitcher.Core/Helpers/ValidationHelpers.cs +++ b/src/JavaScriptEngineSwitcher.Core/Helpers/ValidationHelpers.cs @@ -1,9 +1,11 @@ -namespace JavaScriptEngineSwitcher.Core.Helpers -{ - using System; - using System.Linq; - using System.Text.RegularExpressions; +using System; +using System.Linq; +using System.Text.RegularExpressions; + +using JavaScriptEngineSwitcher.Core.Extensions; +namespace JavaScriptEngineSwitcher.Core.Helpers +{ /// /// Validation helpers /// @@ -20,7 +22,7 @@ public static class ValidationHelpers /// /// List of primitive type codes /// - private static readonly TypeCode[] _primitiveTypeCodes = new[] + private static readonly TypeCode[] _primitiveTypeCodes = { TypeCode.Boolean, TypeCode.SByte, TypeCode.Byte, @@ -30,16 +32,21 @@ public static class ValidationHelpers }; /// - /// Regular expression for working with JS-names + /// Regular expression for working with JS names + /// + private static readonly Regex _jsNameRegex = new Regex("^" + CommonRegExps.JsNamePattern + "$"); + + /// + /// Regular expression for working with document names /// - private static readonly Regex _jsNameRegex = new Regex(@"^[A-Za-z_\$][0-9A-Za-z_\$]*$"); + private static readonly Regex _documentNameRegex = new Regex("^" + CommonRegExps.DocumentNamePattern + "$"); /// /// Checks whether supports a .NET type /// /// .NET type - /// Result of check (true - is supported; false - is not supported) + /// Result of check (true - is supported; false - is not supported) public static bool IsSupportedType(Type type) { bool result = _supportedTypes.Contains(type); @@ -51,10 +58,10 @@ public static bool IsSupportedType(Type type) /// Checks whether .NET type is primitive /// /// .NET type - /// Result of check (true - is primitive; false - is not primitive) + /// Result of check (true - is primitive; false - is not primitive) public static bool IsPrimitiveType(Type type) { - TypeCode typeCode = Type.GetTypeCode(type); + TypeCode typeCode = type.GetTypeCode(); bool result = _primitiveTypeCodes.Contains(typeCode); return result; @@ -64,10 +71,20 @@ public static bool IsPrimitiveType(Type type) /// Checks a format of the name /// /// The name - /// Result of check (true - correct format; false - wrong format) + /// Result of check (true - correct format; false - wrong format) public static bool CheckNameFormat(string name) { return _jsNameRegex.IsMatch(name); } + + /// + /// Checks a format of the document name + /// + /// The document name + /// Result of check (true - correct format; false - wrong format) + public static bool CheckDocumentNameFormat(string name) + { + return _documentNameRegex.IsMatch(name); + } } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/IJsEngine.cs b/src/JavaScriptEngineSwitcher.Core/IJsEngine.cs index 78d89a3f..ba656df0 100644 --- a/src/JavaScriptEngineSwitcher.Core/IJsEngine.cs +++ b/src/JavaScriptEngineSwitcher.Core/IJsEngine.cs @@ -1,16 +1,17 @@ -namespace JavaScriptEngineSwitcher.Core -{ - using System; - using System.Text; - using System.Reflection; +using System; +using System.IO; +using System.Text; +using System.Reflection; +namespace JavaScriptEngineSwitcher.Core +{ /// - /// Interface of JavaScript engine + /// Defines a interface of JS engine /// public interface IJsEngine : IDisposable { /// - /// Gets a name of JavaScript engine + /// Gets a name of JS engine /// string Name { @@ -18,54 +19,267 @@ string Name } /// - /// Gets a version of original JavaScript engine + /// Gets a version of original JS engine /// string Version { get; } + /// + /// Gets a value that indicates if the JS engine supports script pre-compilation + /// + bool SupportsScriptPrecompilation + { + get; + } + + /// + /// Gets a value that indicates if the JS engine supports script interruption + /// + bool SupportsScriptInterruption + { + get; + } + + /// + /// Gets a value that indicates if the JS engine supports garbage collection + /// + bool SupportsGarbageCollection + { + get; + } + + + /// + /// Creates a pre-compiled script from JS code + /// + /// JS code + /// A pre-compiled script that can be executed by different instances of JS engine + /// + /// + /// + /// + /// + IPrecompiledScript Precompile(string code); + + /// + /// Creates a pre-compiled script from JS code + /// + /// JS code + /// Document name + /// A pre-compiled script that can be executed by different instances of JS engine + /// + /// + /// + /// + /// + IPrecompiledScript Precompile(string code, string documentName); + + /// + /// Creates a pre-compiled script from JS file + /// + /// Path to the JS file + /// Text encoding + /// A pre-compiled script that can be executed by different instances of JS engine + /// + /// + /// + /// + /// + /// + /// + IPrecompiledScript PrecompileFile(string path, Encoding encoding = null); + + /// + /// Creates a pre-compiled script from embedded JS resource + /// + /// The case-sensitive resource name without the namespace of the specified type + /// The type, that determines the assembly and whose namespace is used to scope + /// the resource name + /// A pre-compiled script that can be executed by different instances of JS engine + /// + /// + /// + /// + /// + /// + /// + IPrecompiledScript PrecompileResource(string resourceName, Type type); + + /// + /// Creates a pre-compiled script from embedded JS resource + /// + /// The case-sensitive resource name + /// The assembly, which contains the embedded resource + /// A pre-compiled script that can be executed by different instances of JS engine + /// + /// + /// + /// + /// + /// + /// + IPrecompiledScript PrecompileResource(string resourceName, Assembly assembly); /// /// Evaluates an expression /// - /// JS-expression + /// JS expression /// Result of the expression + /// + /// + /// + /// + /// + /// + /// + /// object Evaluate(string expression); + /// + /// Evaluates an expression + /// + /// JS expression + /// Document name + /// Result of the expression + /// + /// + /// + /// + /// + /// + /// + /// + object Evaluate(string expression, string documentName); + /// /// Evaluates an expression /// /// Type of result - /// JS-expression + /// JS expression /// Result of the expression + /// + /// + /// + /// + /// + /// + /// + /// T Evaluate(string expression); + /// + /// Evaluates an expression + /// + /// Type of result + /// JS expression + /// Document name + /// Result of the expression + /// + /// + /// + /// + /// + /// + /// + /// + T Evaluate(string expression, string documentName); + /// /// Executes a code /// - /// Code + /// JS code + /// + /// + /// + /// + /// + /// + /// + /// void Execute(string code); /// - /// Executes a code from JS-file + /// Executes a code /// - /// Path to the JS-file + /// JS code + /// Document name + /// + /// + /// + /// + /// + /// + /// + /// + void Execute(string code, string documentName); + + /// + /// Executes a pre-compiled script + /// + /// A pre-compiled script that can be executed by different + /// instances of JS engine + /// + /// + /// + /// + /// + /// + /// + /// + void Execute(IPrecompiledScript precompiledScript); + + /// + /// Executes a code from JS file + /// + /// Path to the JS file /// Text encoding + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// void ExecuteFile(string path, Encoding encoding = null); /// - /// Executes a code from embedded JS-resource + /// Executes a code from embedded JS resource /// - /// JS-resource name - /// Type from assembly that containing an embedded resource + /// The case-sensitive resource name without the namespace of the specified type + /// The type, that determines the assembly and whose namespace is used to scope + /// the resource name + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// void ExecuteResource(string resourceName, Type type); /// - /// Executes a code from embedded JS-resource + /// Executes a code from embedded JS resource /// - /// JS-resource name - /// Assembly that containing an embedded resource + /// The case-sensitive resource name + /// The assembly, which contains the embedded resource + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// void ExecuteResource(string resourceName, Assembly assembly); /// @@ -74,6 +288,13 @@ string Version /// Function name /// Function arguments /// Result of the function execution + /// + /// + /// + /// + /// + /// + /// object CallFunction(string functionName, params object[] args); /// @@ -83,13 +304,25 @@ string Version /// Function name /// Function arguments /// Result of the function execution + /// + /// + /// + /// + /// + /// + /// T CallFunction(string functionName, params object[] args); /// - /// Сhecks for the existence of a variable + /// Checks for the existence of a variable /// /// Variable name - /// Result of check (true - exists; false - not exists + /// Result of check (true - exists; false - not exists + /// + /// + /// + /// + /// bool HasVariable(string variableName); /// @@ -97,6 +330,11 @@ string Version /// /// Variable name /// Value of variable + /// + /// + /// + /// + /// object GetVariableValue(string variableName); /// @@ -105,6 +343,11 @@ string Version /// Type of variable /// Variable name /// Value of variable + /// + /// + /// + /// + /// T GetVariableValue(string variableName); /// @@ -112,31 +355,61 @@ string Version /// /// Variable name /// Value of variable + /// + /// + /// + /// + /// void SetVariableValue(string variableName, object value); /// /// Removes a variable /// /// Variable name + /// + /// + /// + /// + /// void RemoveVariable(string variableName); /// /// Embeds a host object to script code /// + /// + /// Allows to embed instances of simple classes (or structures) and delegates. + /// /// The name for the new global variable or function that will represent the object /// The object to expose - /// Allows to embed instances of simple classes (or structures) and delegates. + /// + /// + /// + /// void EmbedHostObject(string itemName, object value); /// /// Embeds a host type to script code /// - /// The name for the new global variable that will represent the type - /// The type to expose /// /// Host types are exposed to script code in the form of objects whose properties and /// methods are bound to the type's static members. /// + /// The name for the new global variable that will represent the type + /// The type to expose + /// + /// + /// + /// void EmbedHostType(string itemName, Type type); + + /// + /// Interrupts script execution and causes the JS engine to throw an exception + /// + void Interrupt(); + + /// + /// Performs a full garbage collection + /// + void CollectGarbage(); } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/IJsEngineFactory.cs b/src/JavaScriptEngineSwitcher.Core/IJsEngineFactory.cs new file mode 100644 index 00000000..3a7f328b --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/IJsEngineFactory.cs @@ -0,0 +1,20 @@ +namespace JavaScriptEngineSwitcher.Core +{ + /// + /// Defines a interface of JS engine factory + /// + public interface IJsEngineFactory + { + /// + /// Gets a name of JS engine + /// + string EngineName { get; } + + + /// + /// Creates a instance of JS engine + /// + /// Instance of JS engine + IJsEngine CreateEngine(); + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/IJsEngineSwitcher.cs b/src/JavaScriptEngineSwitcher.Core/IJsEngineSwitcher.cs new file mode 100644 index 00000000..839ef591 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/IJsEngineSwitcher.cs @@ -0,0 +1,39 @@ +namespace JavaScriptEngineSwitcher.Core +{ + /// + /// Defines a interface of JS engine switcher + /// + public interface IJsEngineSwitcher + { + /// + /// Gets or sets a name of default JS engine + /// + string DefaultEngineName + { + get; + set; + } + + /// + /// Gets a collection of JS engine factories + /// + JsEngineFactoryCollection EngineFactories + { + get; + } + + + /// + /// Creates a instance of JS engine + /// + /// JS engine name + /// JS engine + IJsEngine CreateEngine(string name); + + /// + /// Creates a instance of default JS engine + /// + /// JS engine + IJsEngine CreateDefaultEngine(); + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/IPrecompiledScript.cs b/src/JavaScriptEngineSwitcher.Core/IPrecompiledScript.cs new file mode 100644 index 00000000..7b5ec616 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/IPrecompiledScript.cs @@ -0,0 +1,13 @@ +namespace JavaScriptEngineSwitcher.Core +{ + /// + /// Represents a pre-compiled script that can be executed by different instances of the JS engine + /// + public interface IPrecompiledScript + { + /// + /// Gets a name of JS engine for which the pre-compiled script was created + /// + string EngineName { get; } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JavaScriptEngineSwitcher.Core.csproj b/src/JavaScriptEngineSwitcher.Core/JavaScriptEngineSwitcher.Core.csproj index 82c2054a..911456c6 100644 --- a/src/JavaScriptEngineSwitcher.Core/JavaScriptEngineSwitcher.Core.csproj +++ b/src/JavaScriptEngineSwitcher.Core/JavaScriptEngineSwitcher.Core.csproj @@ -1,102 +1,45 @@ - - - + + - Debug - AnyCPU - {5C903EEF-BAD1-43B8-BFE2-E4EE4D204410} + JS Engine Switcher: Core + 3.24.1 + net40-client;net45;netstandard1.3;netstandard2.0 + 1.6.0 Library - Properties - JavaScriptEngineSwitcher.Core - JavaScriptEngineSwitcher.Core - v4.0 - 512 - Client - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true + true + $(NoWarn);CS1591;NETSDK1215;NU1903 + false + true + true + + + + + + - ..\..\JavaScriptEngineSwitcher.snk + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Core_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_Core_Logo128x128.png + JavaScript Engine Switcher determines unified interface for access to the basic features of popular JavaScript engines. This library allows you to quickly and easily switch to using of another JavaScript engine. + $(PackageCommonTags) + - - - + + - - - - - - - - - - - - - - - - - - - True - True - Strings.resx - - - True - True - Strings.ru-ru.resx - - - - - - - - - - JavaScriptEngineSwitcher.snk - + + + + - - PublicResXFileCodeGenerator - Strings.Designer.cs - Designer - - - PublicResXFileCodeGenerator - Strings.ru-ru.Designer.cs - Designer - + - - + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsCompilationException.cs b/src/JavaScriptEngineSwitcher.Core/JsCompilationException.cs new file mode 100644 index 00000000..728437e4 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/JsCompilationException.cs @@ -0,0 +1,80 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using JavaScriptEngineSwitcher.Core.Constants; + +namespace JavaScriptEngineSwitcher.Core +{ + /// + /// The exception that is thrown during the script compilation stage, before the script + /// has begun to be executed + /// +#if !NETSTANDARD1_3 + [Serializable] +#endif + public sealed class JsCompilationException : JsScriptException + { + /// + /// Initializes a new instance of the class + /// with a specified error message + /// + /// The message that describes the error + public JsCompilationException(string message) + : base(message) + { + Category = JsErrorCategory.Compilation; + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception + /// that is the cause of this exception + /// + /// The error message that explains the reason for the exception + /// The exception that is the cause of the current exception + public JsCompilationException(string message, Exception innerException) + : base(message, innerException) + { + Category = JsErrorCategory.Compilation; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + public JsCompilationException(string message, string engineName, string engineVersion) + : base(message, engineName, engineVersion) + { + Category = JsErrorCategory.Compilation; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + /// The exception that is the cause of the current exception + public JsCompilationException(string message, string engineName, string engineVersion, + Exception innerException) + : base(message, engineName, engineVersion, innerException) + { + Category = JsErrorCategory.Compilation; + } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + private JsCompilationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsEngineBase.cs b/src/JavaScriptEngineSwitcher.Core/JsEngineBase.cs index 9aef0c4f..d2efe334 100644 --- a/src/JavaScriptEngineSwitcher.Core/JsEngineBase.cs +++ b/src/JavaScriptEngineSwitcher.Core/JsEngineBase.cs @@ -1,24 +1,31 @@ -namespace JavaScriptEngineSwitcher.Core -{ - using System; - using System.Reflection; - using System.Text; +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; - using Helpers; - using Resources; - using Utilities; +using JavaScriptEngineSwitcher.Core.Helpers; +using JavaScriptEngineSwitcher.Core.Resources; +using JavaScriptEngineSwitcher.Core.Utilities; +namespace JavaScriptEngineSwitcher.Core +{ /// - /// Base class of JavaScript engine + /// Base class of JS engine /// public abstract class JsEngineBase : IJsEngine { + /// + /// Default document name + /// + protected const string DefaultDocumentName = "Script Document"; + /// /// Flag that object is destroyed /// protected InterlockedStatedFlag _disposedFlag = new InterlockedStatedFlag(); + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] protected void VerifyNotDisposed() { if (_disposedFlag.IsSet()) @@ -27,12 +34,33 @@ protected void VerifyNotDisposed() } } + protected virtual IPrecompiledScript InnerPrecompile(string code) + { + throw new NotImplementedException(); + } + + protected virtual IPrecompiledScript InnerPrecompile(string code, string documentName) + { + throw new NotImplementedException(); + } + protected abstract object InnerEvaluate(string expression); + protected abstract object InnerEvaluate(string expression, string documentName); + protected abstract T InnerEvaluate(string expression); + protected abstract T InnerEvaluate(string expression, string documentName); + protected abstract void InnerExecute(string code); + protected abstract void InnerExecute(string code, string documentName); + + protected virtual void InnerExecute(IPrecompiledScript precompiledScript) + { + throw new NotImplementedException(); + } + protected abstract object InnerCallFunction(string functionName, params object[] args); protected abstract T InnerCallFunction(string functionName, params object[] args); @@ -57,133 +85,650 @@ protected virtual void InnerEmbedHostType(string itemName, Type type) throw new NotImplementedException(); } + protected virtual void InnerInterrupt() + { + throw new NotImplementedException(); + } + + protected virtual void InnerCollectGarbage() + { + throw new NotImplementedException(); + } + #region IJsEngine implementation + /// public abstract string Name { get; } + /// public abstract string Version { get; } + /// + public virtual bool SupportsScriptPrecompilation + { + get + { + throw new NotImplementedException(); + } + } + + /// + public virtual bool SupportsScriptInterruption + { + get + { + throw new NotImplementedException(); + } + } + + /// + public virtual bool SupportsGarbageCollection + { + get + { + throw new NotImplementedException(); + } + } + + + /// + public virtual IPrecompiledScript Precompile(string code) + { + VerifyNotDisposed(); + + if (code == null) + { + throw new ArgumentNullException( + nameof(code), + string.Format(Strings.Common_ArgumentIsNull, nameof(code)) + ); + } + + if (string.IsNullOrWhiteSpace(code)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(code)), + nameof(code) + ); + } + + return InnerPrecompile(code); + } + + /// + public virtual IPrecompiledScript Precompile(string code, string documentName) + { + VerifyNotDisposed(); + + if (code == null) + { + throw new ArgumentNullException( + nameof(code), + string.Format(Strings.Common_ArgumentIsNull, nameof(code)) + ); + } + + if (string.IsNullOrWhiteSpace(code)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(code)), + nameof(code) + ); + } + + if (!string.IsNullOrWhiteSpace(documentName) + && !ValidationHelpers.CheckDocumentNameFormat(documentName)) + { + throw new ArgumentException( + string.Format(Strings.Usage_InvalidDocumentNameFormat, documentName), + nameof(documentName) + ); + } + + return InnerPrecompile(code, documentName); + } + + /// + public virtual IPrecompiledScript PrecompileFile(string path, Encoding encoding = null) + { + VerifyNotDisposed(); + + if (path == null) + { + throw new ArgumentNullException( + nameof(path), + string.Format(Strings.Common_ArgumentIsNull, nameof(path)) + ); + } + + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(path)), + nameof(path) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(path)) + { + throw new ArgumentException( + string.Format(Strings.Usage_InvalidFileNameFormat, path), + nameof(path) + ); + } + + string code = Utils.GetFileTextContent(path, encoding); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(Strings.Usage_CannotPrecompileEmptyFile, path), + Name, Version + ); + } + + return InnerPrecompile(code, path); + } + + /// + public virtual IPrecompiledScript PrecompileResource(string resourceName, Type type) + { + VerifyNotDisposed(); + + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(Strings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (type == null) + { + throw new ArgumentNullException( + nameof(type), + string.Format(Strings.Common_ArgumentIsNull, nameof(type)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(Strings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); + } + +#if NET40 + Assembly assembly = type.Assembly; +#else + Assembly assembly = type.GetTypeInfo().Assembly; +#endif + string nameSpace = type.Namespace; + string resourceFullName = nameSpace != null ? nameSpace + "." + resourceName : resourceName; + + string code = Utils.GetResourceAsString(resourceFullName, assembly); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(Strings.Usage_CannotPrecompileEmptyResource, resourceFullName), + Name, Version + ); + } + + return InnerPrecompile(code, resourceName); + } + + /// + public virtual IPrecompiledScript PrecompileResource(string resourceName, Assembly assembly) + { + VerifyNotDisposed(); + + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(Strings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (assembly == null) + { + throw new ArgumentNullException( + nameof(assembly), + string.Format(Strings.Common_ArgumentIsNull, nameof(assembly)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(Strings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); + } + + string code = Utils.GetResourceAsString(resourceName, assembly); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(Strings.Usage_CannotPrecompileEmptyResource, resourceName), + Name, Version + ); + } + + return InnerPrecompile(code, resourceName); + } + /// public virtual object Evaluate(string expression) { VerifyNotDisposed(); + if (expression == null) + { + throw new ArgumentNullException( + nameof(expression), + string.Format(Strings.Common_ArgumentIsNull, nameof(expression)) + ); + } + if (string.IsNullOrWhiteSpace(expression)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "expression"), "expression"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(expression)), + nameof(expression) + ); } return InnerEvaluate(expression); } + /// + public virtual object Evaluate(string expression, string documentName) + { + VerifyNotDisposed(); + + if (expression == null) + { + throw new ArgumentNullException( + nameof(expression), + string.Format(Strings.Common_ArgumentIsNull, nameof(expression)) + ); + } + + if (string.IsNullOrWhiteSpace(expression)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(expression)), + nameof(expression) + ); + } + + if (!string.IsNullOrWhiteSpace(documentName) + && !ValidationHelpers.CheckDocumentNameFormat(documentName)) + { + throw new ArgumentException( + string.Format(Strings.Usage_InvalidDocumentNameFormat, documentName), + nameof(documentName) + ); + } + + return InnerEvaluate(expression, documentName); + } + + /// public virtual T Evaluate(string expression) { VerifyNotDisposed(); + if (expression == null) + { + throw new ArgumentNullException( + nameof(expression), + string.Format(Strings.Common_ArgumentIsNull, nameof(expression)) + ); + } + if (string.IsNullOrWhiteSpace(expression)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "expression"), "expression"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(expression)), + nameof(expression) + ); } Type returnValueType = typeof(T); if (!ValidationHelpers.IsSupportedType(returnValueType)) { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_ReturnValueTypeNotSupported, returnValueType.FullName)); + throw new ArgumentException( + string.Format(Strings.Usage_ReturnValueTypeNotSupported, returnValueType.FullName), + nameof(T) + ); } return InnerEvaluate(expression); } + /// + public virtual T Evaluate(string expression, string documentName) + { + VerifyNotDisposed(); + + if (expression == null) + { + throw new ArgumentNullException( + nameof(expression), + string.Format(Strings.Common_ArgumentIsNull, nameof(expression)) + ); + } + + if (string.IsNullOrWhiteSpace(expression)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(expression)), + nameof(expression) + ); + } + + if (!string.IsNullOrWhiteSpace(documentName) + && !ValidationHelpers.CheckDocumentNameFormat(documentName)) + { + throw new ArgumentException( + string.Format(Strings.Usage_InvalidDocumentNameFormat, documentName), + nameof(documentName) + ); + } + + Type returnValueType = typeof(T); + if (!ValidationHelpers.IsSupportedType(returnValueType)) + { + throw new ArgumentException( + string.Format(Strings.Usage_ReturnValueTypeNotSupported, returnValueType.FullName), + nameof(T) + ); + } + + return InnerEvaluate(expression, documentName); + } + + /// public virtual void Execute(string code) { VerifyNotDisposed(); + if (code == null) + { + throw new ArgumentNullException( + nameof(code), + string.Format(Strings.Common_ArgumentIsNull, nameof(code)) + ); + } + if (string.IsNullOrWhiteSpace(code)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "code"), "code"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(code)), + nameof(code) + ); } InnerExecute(code); } + /// + public virtual void Execute(string code, string documentName) + { + VerifyNotDisposed(); + + if (code == null) + { + throw new ArgumentNullException( + nameof(code), + string.Format(Strings.Common_ArgumentIsNull, nameof(code)) + ); + } + + if (string.IsNullOrWhiteSpace(code)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(code)), + nameof(code) + ); + } + + if (!string.IsNullOrWhiteSpace(documentName) + && !ValidationHelpers.CheckDocumentNameFormat(documentName)) + { + throw new ArgumentException( + string.Format(Strings.Usage_InvalidDocumentNameFormat, documentName), + nameof(documentName) + ); + } + + InnerExecute(code, documentName); + } + + /// + public virtual void Execute(IPrecompiledScript precompiledScript) + { + VerifyNotDisposed(); + + if (precompiledScript == null) + { + throw new ArgumentNullException( + nameof(precompiledScript), + string.Format(Strings.Common_ArgumentIsNull, nameof(precompiledScript)) + ); + } + + if (precompiledScript.EngineName != Name) + { + throw new JsUsageException( + string.Format(Strings.Usage_CannotExecutePrecompiledScriptForAnotherJsEngine, + precompiledScript.EngineName), + Name, Version + ); + } + + InnerExecute(precompiledScript); + } + + /// public virtual void ExecuteFile(string path, Encoding encoding = null) { VerifyNotDisposed(); + if (path == null) + { + throw new ArgumentNullException( + nameof(path), + string.Format(Strings.Common_ArgumentIsNull, nameof(path)) + ); + } + if (string.IsNullOrWhiteSpace(path)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "path"), "path"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(path)), + nameof(path) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(path)) + { + throw new ArgumentException( + string.Format(Strings.Usage_InvalidFileNameFormat, path), + nameof(path) + ); } string code = Utils.GetFileTextContent(path, encoding); - Execute(code); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(Strings.Usage_CannotExecuteEmptyFile, path), + Name, Version + ); + } + + InnerExecute(code, path); } + /// public virtual void ExecuteResource(string resourceName, Type type) { VerifyNotDisposed(); - if (string.IsNullOrWhiteSpace(resourceName)) + if (resourceName == null) { - throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "resourceName"), "resourceName"); + throw new ArgumentNullException( + nameof(resourceName), + string.Format(Strings.Common_ArgumentIsNull, nameof(resourceName)) + ); } if (type == null) { throw new ArgumentNullException( - "type", string.Format(Strings.Common_ArgumentIsNull, "type")); + nameof(type), + string.Format(Strings.Common_ArgumentIsNull, nameof(type)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); } - string code = Utils.GetResourceAsString(resourceName, type); - Execute(code); + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(Strings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); + } + +#if NET40 + Assembly assembly = type.Assembly; +#else + Assembly assembly = type.GetTypeInfo().Assembly; +#endif + string nameSpace = type.Namespace; + string resourceFullName = nameSpace != null ? nameSpace + "." + resourceName : resourceName; + + string code = Utils.GetResourceAsString(resourceFullName, assembly); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(Strings.Usage_CannotExecuteEmptyResource, resourceFullName), + Name, Version + ); + } + + InnerExecute(code, resourceName); } + /// public virtual void ExecuteResource(string resourceName, Assembly assembly) { VerifyNotDisposed(); - if (string.IsNullOrWhiteSpace(resourceName)) + if (resourceName == null) { - throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "resourceName"), "resourceName"); + throw new ArgumentNullException( + nameof(resourceName), + string.Format(Strings.Common_ArgumentIsNull, nameof(resourceName)) + ); } if (assembly == null) { throw new ArgumentNullException( - "assembly", string.Format(Strings.Common_ArgumentIsNull, "assembly")); + nameof(assembly), + string.Format(Strings.Common_ArgumentIsNull, nameof(assembly)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(Strings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); } string code = Utils.GetResourceAsString(resourceName, assembly); - Execute(code); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(Strings.Usage_CannotExecuteEmptyResource, resourceName), + Name, Version + ); + } + + InnerExecute(code, resourceName); } + /// public virtual object CallFunction(string functionName, params object[] args) { VerifyNotDisposed(); + if (functionName == null) + { + throw new ArgumentNullException( + nameof(functionName), + string.Format(Strings.Common_ArgumentIsNull, nameof(functionName)) + ); + } + if (string.IsNullOrWhiteSpace(functionName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "functionName"), "functionName"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(functionName)), + nameof(functionName) + ); } if (!ValidationHelpers.CheckNameFormat(functionName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidFunctionNameFormat, functionName)); + throw new ArgumentException( + string.Format(Strings.Usage_InvalidFunctionNameFormat, nameof(functionName)), + nameof(functionName) + ); } int argumentCount = args.Length; @@ -199,9 +744,11 @@ public virtual object CallFunction(string functionName, params object[] args) if (!ValidationHelpers.IsSupportedType(argType)) { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_FunctionParameterTypeNotSupported, - functionName, argType.FullName)); + throw new ArgumentException( + string.Format(Strings.Usage_FunctionParameterTypeNotSupported, + functionName, argType.FullName), + nameof(args) + ); } } } @@ -210,27 +757,33 @@ public virtual object CallFunction(string functionName, params object[] args) return InnerCallFunction(functionName, args); } + /// public virtual T CallFunction(string functionName, params object[] args) { VerifyNotDisposed(); - if (string.IsNullOrWhiteSpace(functionName)) + if (functionName == null) { - throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "functionName"), "functionName"); + throw new ArgumentNullException( + nameof(functionName), + string.Format(Strings.Common_ArgumentIsNull, nameof(functionName)) + ); } - Type returnValueType = typeof(T); - if (!ValidationHelpers.IsSupportedType(returnValueType)) + if (string.IsNullOrWhiteSpace(functionName)) { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_ReturnValueTypeNotSupported, returnValueType.FullName)); + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(functionName)), + nameof(functionName) + ); } if (!ValidationHelpers.CheckNameFormat(functionName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidFunctionNameFormat, functionName)); + throw new ArgumentException( + string.Format(Strings.Usage_InvalidFunctionNameFormat, functionName), + nameof(functionName) + ); } int argumentCount = args.Length; @@ -246,95 +799,160 @@ public virtual T CallFunction(string functionName, params object[] args) if (!ValidationHelpers.IsSupportedType(argType)) { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_FunctionParameterTypeNotSupported, - functionName, argType.FullName)); + throw new ArgumentException( + string.Format(Strings.Usage_FunctionParameterTypeNotSupported, + functionName, argType.FullName), + nameof(args) + ); } } } } + Type returnValueType = typeof(T); + if (!ValidationHelpers.IsSupportedType(returnValueType)) + { + throw new ArgumentException( + string.Format(Strings.Usage_ReturnValueTypeNotSupported, returnValueType.FullName), + nameof(T) + ); + } + return InnerCallFunction(functionName, args); } + /// public virtual bool HasVariable(string variableName) { VerifyNotDisposed(); + if (variableName == null) + { + throw new ArgumentNullException( + nameof(variableName), + string.Format(Strings.Common_ArgumentIsNull, nameof(variableName)) + ); + } + if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "variableName"), "variableName"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(variableName)), + nameof(variableName) + ); } if (!ValidationHelpers.CheckNameFormat(variableName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidVariableNameFormat, variableName)); + throw new ArgumentException( + string.Format(Strings.Usage_InvalidVariableNameFormat, variableName), + nameof(variableName) + ); } return InnerHasVariable(variableName); } + /// public virtual object GetVariableValue(string variableName) { VerifyNotDisposed(); + if (variableName == null) + { + throw new ArgumentNullException( + nameof(variableName), + string.Format(Strings.Common_ArgumentIsNull, nameof(variableName)) + ); + } + if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "variableName"), "variableName"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(variableName)), + nameof(variableName) + ); } if (!ValidationHelpers.CheckNameFormat(variableName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidVariableNameFormat, variableName)); + throw new ArgumentException( + string.Format(Strings.Usage_InvalidVariableNameFormat, variableName), + nameof(variableName) + ); } return InnerGetVariableValue(variableName); } + /// public virtual T GetVariableValue(string variableName) { VerifyNotDisposed(); + if (variableName == null) + { + throw new ArgumentNullException( + nameof(variableName), + string.Format(Strings.Common_ArgumentIsNull, nameof(variableName)) + ); + } + if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "variableName"), "variableName"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(variableName)), + nameof(variableName) + ); } - Type returnValueType = typeof(T); - if (!ValidationHelpers.IsSupportedType(returnValueType)) + if (!ValidationHelpers.CheckNameFormat(variableName)) { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_ReturnValueTypeNotSupported, returnValueType.FullName)); + throw new ArgumentException( + string.Format(Strings.Usage_InvalidVariableNameFormat, variableName), + nameof(variableName) + ); } - if (!ValidationHelpers.CheckNameFormat(variableName)) + Type returnValueType = typeof(T); + if (!ValidationHelpers.IsSupportedType(returnValueType)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidVariableNameFormat, variableName)); + throw new ArgumentException( + string.Format(Strings.Usage_ReturnValueTypeNotSupported, returnValueType.FullName), + nameof(T) + ); } return InnerGetVariableValue(variableName); } + /// public virtual void SetVariableValue(string variableName, object value) { VerifyNotDisposed(); + if (variableName == null) + { + throw new ArgumentNullException( + nameof(variableName), + string.Format(Strings.Common_ArgumentIsNull, nameof(variableName)) + ); + } + if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "variableName"), "variableName"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(variableName)), + nameof(variableName) + ); } if (!ValidationHelpers.CheckNameFormat(variableName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidVariableNameFormat, variableName)); + throw new ArgumentException( + string.Format(Strings.Usage_InvalidVariableNameFormat, variableName), + nameof(variableName) + ); } if (value != null) @@ -343,103 +961,165 @@ public virtual void SetVariableValue(string variableName, object value) if (!ValidationHelpers.IsSupportedType(variableType)) { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_VariableTypeNotSupported, - variableName, variableType.FullName)); + throw new ArgumentException( + string.Format(Strings.Usage_VariableTypeNotSupported, + variableName, variableType.FullName), + nameof(value) + ); } } InnerSetVariableValue(variableName, value); } + /// public virtual void RemoveVariable(string variableName) { VerifyNotDisposed(); + if (variableName == null) + { + throw new ArgumentNullException( + nameof(variableName), + string.Format(Strings.Common_ArgumentIsNull, nameof(variableName)) + ); + } + if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "variableName"), "variableName"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(variableName)), + nameof(variableName) + ); } if (!ValidationHelpers.CheckNameFormat(variableName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidVariableNameFormat, variableName)); + throw new ArgumentException( + string.Format(Strings.Usage_InvalidVariableNameFormat, variableName), + nameof(variableName) + ); } InnerRemoveVariable(variableName); } + /// public virtual void EmbedHostObject(string itemName, object value) { VerifyNotDisposed(); + if (itemName == null) + { + throw new ArgumentNullException( + nameof(itemName), + string.Format(Strings.Common_ArgumentIsNull, nameof(itemName)) + ); + } + + if (value == null) + { + throw new ArgumentNullException( + nameof(value), + string.Format(Strings.Common_ArgumentIsNull, nameof(value)) + ); + } + if (string.IsNullOrWhiteSpace(itemName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "itemName"), "itemName"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(itemName)), + nameof(itemName) + ); } if (!ValidationHelpers.CheckNameFormat(itemName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidScriptItemNameFormat, itemName)); + throw new ArgumentException( + string.Format(Strings.Usage_InvalidScriptItemNameFormat, itemName), + nameof(itemName) + ); } - if (value != null) - { - Type itemType = value.GetType(); + Type itemType = value.GetType(); - if (ValidationHelpers.IsPrimitiveType(itemType) - || itemType == typeof(Undefined) - || itemType == typeof(DateTime)) - { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_EmbeddedHostObjectTypeNotSupported, itemName, itemType.FullName)); - } - } - else + if (ValidationHelpers.IsPrimitiveType(itemType) + || itemType == typeof(Undefined) + || itemType == typeof(DateTime)) { - throw new ArgumentNullException("value", string.Format(Strings.Common_ArgumentIsNull, "value")); + throw new ArgumentException( + string.Format(Strings.Usage_EmbeddedHostObjectTypeNotSupported, itemName, itemType.FullName), + nameof(value) + ); } InnerEmbedHostObject(itemName, value); } + /// public virtual void EmbedHostType(string itemName, Type type) { VerifyNotDisposed(); + if (itemName == null) + { + throw new ArgumentNullException( + nameof(itemName), + string.Format(Strings.Common_ArgumentIsNull, nameof(itemName)) + ); + } + + if (type == null) + { + throw new ArgumentNullException( + nameof(type), + string.Format(Strings.Common_ArgumentIsNull, nameof(type)) + ); + } + if (string.IsNullOrWhiteSpace(itemName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "itemName"), "itemName"); + string.Format(Strings.Common_ArgumentIsEmpty, nameof(itemName)), + nameof(itemName) + ); } if (!ValidationHelpers.CheckNameFormat(itemName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidScriptItemNameFormat, itemName)); + throw new ArgumentException( + string.Format(Strings.Usage_InvalidScriptItemNameFormat, itemName), + nameof(itemName) + ); } - if (type != null) - { - if (ValidationHelpers.IsPrimitiveType(type) - || type == typeof(Undefined)) - { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_EmbeddedHostTypeNotSupported, type.FullName)); - } - } - else + if (ValidationHelpers.IsPrimitiveType(type) || type == typeof(Undefined)) { - throw new ArgumentNullException("type", string.Format(Strings.Common_ArgumentIsNull, "type")); + throw new ArgumentException( + string.Format(Strings.Usage_EmbeddedHostTypeNotSupported, type.FullName), + nameof(type) + ); } InnerEmbedHostType(itemName, type); } + /// + public virtual void Interrupt() + { + VerifyNotDisposed(); + + InnerInterrupt(); + } + + /// + public virtual void CollectGarbage() + { + VerifyNotDisposed(); + + InnerCollectGarbage(); + } + #endregion #region IDisposable implementation diff --git a/src/JavaScriptEngineSwitcher.Core/JsEngineException.cs b/src/JavaScriptEngineSwitcher.Core/JsEngineException.cs new file mode 100644 index 00000000..9a97e59e --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/JsEngineException.cs @@ -0,0 +1,79 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using JavaScriptEngineSwitcher.Core.Constants; + +namespace JavaScriptEngineSwitcher.Core +{ + /// + /// The exception that occurred in the workings of the JS engine itself + /// +#if !NETSTANDARD1_3 + [Serializable] +#endif + public class JsEngineException : JsException + { + /// + /// Initializes a new instance of the class + /// with a specified error message + /// + /// The message that describes the error + public JsEngineException(string message) + : base(message) + { + Category = JsErrorCategory.Engine; + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception + /// that is the cause of this exception + /// + /// The error message that explains the reason for the exception + /// The exception that is the cause of the current exception + public JsEngineException(string message, Exception innerException) + : base(message, innerException) + { + Category = JsErrorCategory.Engine; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + public JsEngineException(string message, string engineName, string engineVersion) + : base(message, engineName, engineVersion) + { + Category = JsErrorCategory.Engine; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + /// The exception that is the cause of the current exception + public JsEngineException(string message, string engineName, string engineVersion, + Exception innerException) + : base(message, engineName, engineVersion, innerException) + { + Category = JsErrorCategory.Engine; + } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + protected JsEngineException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsEngineFactoryCollection.cs b/src/JavaScriptEngineSwitcher.Core/JsEngineFactoryCollection.cs new file mode 100644 index 00000000..632edba2 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/JsEngineFactoryCollection.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace JavaScriptEngineSwitcher.Core +{ + /// + /// Collection of JS engine factories + /// + public sealed class JsEngineFactoryCollection : IEnumerable + { + /// + /// Dictionary of factories + /// + private readonly Dictionary _factories = + new Dictionary(); + + /// + /// Gets a number of factories in the collection + /// + public int Count + { + get { return _factories.Count; } + } + + + /// + /// Gets all registered factories + /// + /// A read-only collection of all factories in the collection + public ReadOnlyCollection GetRegisteredFactories() + { + return new List(_factories.Values).AsReadOnly(); + } + + /// + /// Gets a factory by JS engine name + /// + /// Name of JS engine + /// Instance of corresponding JS engine factory or null if factory is not found + public IJsEngineFactory Get(string engineName) + { + if (_factories.ContainsKey(engineName)) + { + return _factories[engineName]; + } + + return null; + } + + /// + /// Adds a factory to the collection + /// + /// The factory to add to the collection + public void Add(IJsEngineFactory factory) + { + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + string engineName = factory.EngineName; + if (_factories.ContainsKey(engineName)) + { + _factories[engineName] = factory; + } + else + { + _factories.Add(engineName, factory); + } + } + + /// + /// Removes a single factory from the collection + /// + /// Name of JS engine + /// A boolean value indicating whether the factory was succesfully removed from the collection + public bool Remove(string engineName) + { + if (engineName == null) + { + throw new ArgumentNullException(nameof(engineName)); + } + + return _factories.Remove(engineName); + } + + /// + /// Removes a single factory from the collection + /// + /// The factory to remove from the collection + /// A boolean value indicating whether the factory was succesfully removed from the collection + public bool Remove(IJsEngineFactory factory) + { + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + return _factories.Remove(factory.EngineName); + } + + /// + /// Removes all factories from the collection + /// + public void Clear() + { + _factories.Clear(); + } + + #region IEnumerable implementation + + public IEnumerator GetEnumerator() + { + return _factories.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _factories.Values.GetEnumerator(); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsEngineLoadException.cs b/src/JavaScriptEngineSwitcher.Core/JsEngineLoadException.cs index 7f6d429c..9d84a9d0 100644 --- a/src/JavaScriptEngineSwitcher.Core/JsEngineLoadException.cs +++ b/src/JavaScriptEngineSwitcher.Core/JsEngineLoadException.cs @@ -1,11 +1,19 @@ -namespace JavaScriptEngineSwitcher.Core -{ - using System; +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using JavaScriptEngineSwitcher.Core.Constants; +namespace JavaScriptEngineSwitcher.Core +{ /// - /// The exception that is thrown when a loading of JavaScript engine is failed + /// The exception that is thrown when a loading of JS engine is failed /// - public sealed class JsEngineLoadException : JsException +#if !NETSTANDARD1_3 + [Serializable] +#endif + public sealed class JsEngineLoadException : JsEngineException { /// /// Initializes a new instance of the class @@ -13,41 +21,59 @@ public sealed class JsEngineLoadException : JsException /// /// The message that describes the error public JsEngineLoadException(string message) - : this(message, null) - { } + : base(message) + { + Category = JsErrorCategory.EngineLoad; + } /// /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that is the cause of this exception + /// with a specified error message and a reference to the inner exception + /// that is the cause of this exception /// /// The error message that explains the reason for the exception /// The exception that is the cause of the current exception public JsEngineLoadException(string message, Exception innerException) - : this(message, string.Empty, string.Empty, innerException) - { } + : base(message, innerException) + { + Category = JsErrorCategory.EngineLoad; + } /// /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that is the cause of this exception /// /// The error message that explains the reason for the exception - /// Name of JavaScript engine - /// Version of original JavaScript engine + /// Name of JS engine + /// Version of original JS engine public JsEngineLoadException(string message, string engineName, string engineVersion) - : this(message, engineName, engineVersion, null) - { } + : base(message, engineName, engineVersion) + { + Category = JsErrorCategory.EngineLoad; + } /// /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that is the cause of this exception /// /// The error message that explains the reason for the exception - /// Name of JavaScript engine - /// Version of original JavaScript engine + /// Name of JS engine + /// Version of original JS engine /// The exception that is the cause of the current exception public JsEngineLoadException(string message, string engineName, string engineVersion, Exception innerException) : base(message, engineName, engineVersion, innerException) + { + Category = JsErrorCategory.EngineLoad; + } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + private JsEngineLoadException(SerializationInfo info, StreamingContext context) + : base(info, context) { } +#endif } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsEngineNotFoundException.cs b/src/JavaScriptEngineSwitcher.Core/JsEngineNotFoundException.cs index 1919ee76..7a9b37b4 100644 --- a/src/JavaScriptEngineSwitcher.Core/JsEngineNotFoundException.cs +++ b/src/JavaScriptEngineSwitcher.Core/JsEngineNotFoundException.cs @@ -1,10 +1,16 @@ -namespace JavaScriptEngineSwitcher.Core -{ - using System; +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif +namespace JavaScriptEngineSwitcher.Core +{ /// - /// The exception that is thrown when a JavaScript engine is not found + /// The exception that is thrown when a JS engine is not found /// +#if !NETSTANDARD1_3 + [Serializable] +#endif public sealed class JsEngineNotFoundException : Exception { /// @@ -18,12 +24,24 @@ public JsEngineNotFoundException(string message) /// /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that is the cause of this exception + /// with a specified error message and a reference to the inner exception + /// that is the cause of this exception /// /// The error message that explains the reason for the exception /// The exception that is the cause of the current exception public JsEngineNotFoundException(string message, Exception innerException) : base(message, innerException) { } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + private JsEngineNotFoundException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsEngineSwitcher.cs b/src/JavaScriptEngineSwitcher.Core/JsEngineSwitcher.cs index 3a78ff1c..6ccf8d9f 100644 --- a/src/JavaScriptEngineSwitcher.Core/JsEngineSwitcher.cs +++ b/src/JavaScriptEngineSwitcher.Core/JsEngineSwitcher.cs @@ -1,86 +1,154 @@ -namespace JavaScriptEngineSwitcher.Core -{ - using System; - using System.Configuration; +using System; - using Configuration; - using Resources; - using Utilities; +using JavaScriptEngineSwitcher.Core.Resources; +namespace JavaScriptEngineSwitcher.Core +{ /// - /// JavaScript engine switcher + /// JS engine switcher /// - public sealed class JsEngineSwitcher + public sealed class JsEngineSwitcher : IJsEngineSwitcher { /// - /// Instance of JavaScript engine switcher + /// Flag for whether to allow usage of the property + /// + private static bool _allowCurrentProperty = true; + + /// + /// Default instance of JS engine switcher + /// + private static readonly Lazy _default + = new Lazy(() => new JsEngineSwitcher()); + + /// + /// Current instance of JS engine switcher + /// + private static IJsEngineSwitcher _current; + + /// + /// Gets or sets a flag for whether to allow usage of the property /// - private static readonly Lazy _instance = - new Lazy(() => new JsEngineSwitcher()); + /// + /// Required to ensure the usage of an instance of the JS engine switcher that is registered by using + /// the IServiceCollection interface. + /// + public static bool AllowCurrentProperty + { + get { return _allowCurrentProperty; } + set { _allowCurrentProperty = value; } + } /// - /// Configuration settings of core + /// Gets or sets a instance of JS engine switcher /// - private readonly Lazy _coreConfig = - new Lazy(() => - (CoreConfiguration)ConfigurationManager.GetSection("jsEngineSwitcher/core")); + public static IJsEngineSwitcher Current + { + get + { + if (!_allowCurrentProperty) + { + throw new InvalidOperationException( + Strings.Configuration_GettingFromJsEngineSwitcherCurrentPropertyForbidden); + } + + return _current ?? _default.Value; + } + set + { + if (!_allowCurrentProperty) + { + throw new InvalidOperationException( + Strings.Configuration_AssigningToJsEngineSwitcherCurrentPropertyForbidden); + } + + _current = value; + } + } /// - /// Gets a instance of JavaScript engine switcher + /// Gets a instance of JS engine switcher /// - public static JsEngineSwitcher Current + [Obsolete("Use a `Current` property")] + public static IJsEngineSwitcher Instance { - get { return _instance.Value; } + get { return Current; } } /// - /// Private constructor for implementation Singleton pattern + /// Constructs an instance of JS engine switcher /// - private JsEngineSwitcher() + public JsEngineSwitcher() + : this(new JsEngineFactoryCollection()) { } + /// + /// Constructs an instance of JS engine switcher + /// + public JsEngineSwitcher(JsEngineFactoryCollection engineFactories) + : this(engineFactories, string.Empty) + { } /// - /// Creates a instance of JavaScript engine + /// Constructs an instance of JS engine switcher /// - /// JavaScript engine name - /// JavaScript engine - public IJsEngine CreateJsEngineInstance(string name) + public JsEngineSwitcher(JsEngineFactoryCollection engineFactories, string defaultEngineName) + { + EngineFactories = engineFactories; + DefaultEngineName = defaultEngineName; + } + + + #region IJsEngineSwitcher implementation + + /// + public string DefaultEngineName + { + get; + set; + } + + /// + public JsEngineFactoryCollection EngineFactories + { + get; + private set; + } + + + /// + public IJsEngine CreateEngine(string name) { - IJsEngine jsEngine; - JsEngineRegistrationList jsEngineRegistrationList = _coreConfig.Value.Engines; - JsEngineRegistration jsEngineRegistration = jsEngineRegistrationList[name]; + IJsEngine engine; + IJsEngineFactory engineFactory = EngineFactories.Get(name); - if (jsEngineRegistration != null) + if (engineFactory != null) { - jsEngine = Utils.CreateInstanceByFullTypeName(jsEngineRegistration.Type); + engine = engineFactory.CreateEngine(); } else { throw new JsEngineNotFoundException( - string.Format(Strings.Configuration_JsEngineNotRegistered, name)); + string.Format(Strings.Configuration_JsEngineFactoryNotFound, name)); } - return jsEngine; + return engine; } - /// - /// Creates a instance of default JavaScript engine based on the settings - /// that specified in configuration files (App.config or Web.config) - /// - /// JavaScript engine - public IJsEngine CreateDefaultJsEngineInstance() + /// + public IJsEngine CreateDefaultEngine() { - string defaultJsEngineName = _coreConfig.Value.DefaultEngine; + string defaultJsEngineName = DefaultEngineName; if (string.IsNullOrWhiteSpace(defaultJsEngineName)) { - throw new ConfigurationErrorsException(Strings.Configuration_DefaultJsEngineNotSpecified); + throw new InvalidOperationException(Strings.Configuration_DefaultJsEngineNameNotSpecified); } - IJsEngine jsEngine = CreateJsEngineInstance(defaultJsEngineName); + IJsEngine jsEngine = CreateEngine(defaultJsEngineName); return jsEngine; } + + #endregion } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsException.cs b/src/JavaScriptEngineSwitcher.Core/JsException.cs index 8d2c876a..47ce344f 100644 --- a/src/JavaScriptEngineSwitcher.Core/JsException.cs +++ b/src/JavaScriptEngineSwitcher.Core/JsException.cs @@ -1,28 +1,77 @@ -namespace JavaScriptEngineSwitcher.Core -{ - using System; +using System; +using System.Text; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +using System.Security.Permissions; +#endif + +using AdvancedStringBuilder; +using JavaScriptEngineSwitcher.Core.Constants; +using JavaScriptEngineSwitcher.Core.Helpers; + +namespace JavaScriptEngineSwitcher.Core +{ /// - /// The exception that is thrown during the work of JavaScript engine + /// The exception that is thrown during the work of JS engine /// +#if !NETSTANDARD1_3 + [Serializable] +#endif public class JsException : Exception { /// - /// Gets a name of JavaScript engine + /// Name of JS engine + /// + private readonly string _engineName = string.Empty; + + /// + /// Version of original JS engine + /// + private readonly string _engineVersion = string.Empty; + + /// + /// Error category + /// + private string _category = JsErrorCategory.Unknown; + + /// + /// Description of error + /// + private string _description = string.Empty; + + /// + /// Gets a name of JS engine /// public string EngineName { - get; - private set; + get { return _engineName; } } /// - /// Gets a version of original JavaScript engine + /// Gets a version of original JS engine /// public string EngineVersion { - get; - private set; + get { return _engineVersion; } + } + + /// + /// Gets or sets a error category + /// + public string Category + { + get { return _category; } + set { _category = value; } + } + + /// + /// Gets or sets a description of error + /// + public string Description + { + get { return _description; } + set { _description = value; } } @@ -32,43 +81,129 @@ public string EngineVersion /// /// The message that describes the error public JsException(string message) - : this(message, string.Empty, string.Empty) + : base(message) { } /// /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that is the cause of this exception + /// with a specified error message and a reference to the inner exception + /// that is the cause of this exception /// /// The error message that explains the reason for the exception /// The exception that is the cause of the current exception public JsException(string message, Exception innerException) - : this(message, string.Empty, string.Empty, innerException) + : base(message, innerException) { } /// /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that is the cause of this exception /// /// The error message that explains the reason for the exception - /// Name of JavaScript engine - /// Version of original JavaScript engine + /// Name of JS engine + /// Version of original JS engine public JsException(string message, string engineName, string engineVersion) : this(message, engineName, engineVersion, null) { } /// /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that is the cause of this exception /// /// The error message that explains the reason for the exception - /// Name of JavaScript engine - /// Version of original JavaScript engine + /// Name of JS engine + /// Version of original JS engine /// The exception that is the cause of the current exception public JsException(string message, string engineName, string engineVersion, Exception innerException) : base(message, innerException) { - EngineName = engineName; - EngineVersion = engineVersion; + _engineName = engineName; + _engineVersion = engineVersion; } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + protected JsException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info != null) + { + _engineName = info.GetString("EngineName"); + _engineVersion = info.GetString("EngineVersion"); + _category = info.GetString("Category"); + _description = info.GetString("Description"); + } + } + + + #region Exception overrides + + /// + /// Populates a with the data needed to serialize the target object + /// + /// The to populate with data + /// The destination (see ) for this serialization + [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + base.GetObjectData(info, context); + info.AddValue("EngineName", _engineName); + info.AddValue("EngineVersion", _engineVersion); + info.AddValue("Category", _category); + info.AddValue("Description", _description); + } + + #endregion +#endif + + #region Object overrides + + /// + /// Returns a string that represents the current exception + /// + /// A string that represents the current exception + public override string ToString() + { + string errorDetails = JsErrorHelpers.GenerateErrorDetails(this, true); + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder resultBuilder = stringBuilderPool.Rent(); + resultBuilder.Append(this.GetType().FullName); + resultBuilder.Append(": "); + resultBuilder.Append(this.Message); + + if (errorDetails.Length > 0) + { + resultBuilder.AppendLine(); + resultBuilder.AppendLine(); + resultBuilder.Append(errorDetails); + } + + if (this.InnerException != null) + { + resultBuilder.Append(" ---> "); + resultBuilder.Append(this.InnerException.ToString()); + } + + if (this.StackTrace != null) + { + resultBuilder.AppendLine(); + resultBuilder.AppendLine(this.StackTrace); + } + + string result = resultBuilder.ToString(); + stringBuilderPool.Return(resultBuilder); + + return result; + } + + #endregion } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsFatalException.cs b/src/JavaScriptEngineSwitcher.Core/JsFatalException.cs new file mode 100644 index 00000000..36030448 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/JsFatalException.cs @@ -0,0 +1,79 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using JavaScriptEngineSwitcher.Core.Constants; + +namespace JavaScriptEngineSwitcher.Core +{ + /// + /// The fatal exception occurred + /// +#if !NETSTANDARD1_3 + [Serializable] +#endif + public sealed class JsFatalException : JsException + { + /// + /// Initializes a new instance of the class + /// with a specified error message + /// + /// The message that describes the error + public JsFatalException(string message) + : base(message) + { + Category = JsErrorCategory.Fatal; + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception + /// that is the cause of this exception + /// + /// The error message that explains the reason for the exception + /// The exception that is the cause of the current exception + public JsFatalException(string message, Exception innerException) + : base(message, innerException) + { + Category = JsErrorCategory.Fatal; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + public JsFatalException(string message, string engineName, string engineVersion) + : base(message, engineName, engineVersion) + { + Category = JsErrorCategory.Fatal; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + /// The exception that is the cause of the current exception + public JsFatalException(string message, string engineName, string engineVersion, + Exception innerException) + : base(message, engineName, engineVersion, innerException) + { + Category = JsErrorCategory.Fatal; + } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + private JsFatalException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsInterruptedException.cs b/src/JavaScriptEngineSwitcher.Core/JsInterruptedException.cs new file mode 100644 index 00000000..aa1e5b22 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/JsInterruptedException.cs @@ -0,0 +1,79 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using JavaScriptEngineSwitcher.Core.Constants; + +namespace JavaScriptEngineSwitcher.Core +{ + /// + /// The exception that is thrown when script execution is interrupted by the host + /// +#if !NETSTANDARD1_3 + [Serializable] +#endif + public sealed class JsInterruptedException : JsRuntimeException + { + /// + /// Initializes a new instance of the class + /// with a specified error message + /// + /// The message that describes the error + public JsInterruptedException(string message) + : base(message) + { + Category = JsErrorCategory.Interrupted; + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception + /// that is the cause of this exception + /// + /// The error message that explains the reason for the exception + /// The exception that is the cause of the current exception + public JsInterruptedException(string message, Exception innerException) + : base(message, innerException) + { + Category = JsErrorCategory.Interrupted; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + public JsInterruptedException(string message, string engineName, string engineVersion) + : base(message, engineName, engineVersion) + { + Category = JsErrorCategory.Interrupted; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + /// The exception that is the cause of the current exception + public JsInterruptedException(string message, string engineName, string engineVersion, + Exception innerException) + : base(message, engineName, engineVersion, innerException) + { + Category = JsErrorCategory.Interrupted; + } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + private JsInterruptedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsRuntimeException.cs b/src/JavaScriptEngineSwitcher.Core/JsRuntimeException.cs index b7e4af4d..0bad7ae9 100644 --- a/src/JavaScriptEngineSwitcher.Core/JsRuntimeException.cs +++ b/src/JavaScriptEngineSwitcher.Core/JsRuntimeException.cs @@ -1,56 +1,35 @@ -namespace JavaScriptEngineSwitcher.Core -{ - using System; +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +using System.Security.Permissions; +#endif + +using JavaScriptEngineSwitcher.Core.Constants; +namespace JavaScriptEngineSwitcher.Core +{ /// - /// The exception that is thrown during a execution of code by JavaScript engine + /// The exception that is thrown during the script execution /// - public sealed class JsRuntimeException : JsException +#if !NETSTANDARD1_3 + [Serializable] +#endif + public class JsRuntimeException : JsScriptException { /// - /// Gets or sets a error code - /// - public string ErrorCode - { - get; - set; - } - - /// - /// Gets or sets a error category - /// - public string Category - { - get; - set; - } - - /// - /// Gets or sets a line number + /// String representation of the script call stack /// - public int LineNumber - { - get; - set; - } + private string _callStack = string.Empty; /// - /// Gets or sets a column number + /// Gets or sets a string representation of the script call stack /// - public int ColumnNumber + public string CallStack { - get; - set; + get { return _callStack; } + set { _callStack = value; } } - /// - /// Gets or sets a source fragment - /// - public string SourceFragment - { - get; - set; - } /// /// Initializes a new instance of the class @@ -58,47 +37,86 @@ public string SourceFragment /// /// The message that describes the error public JsRuntimeException(string message) - : this(message, null) - { } + : base(message) + { + Category = JsErrorCategory.Runtime; + } /// /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that is the cause of this exception + /// with a specified error message and a reference to the inner exception + /// that is the cause of this exception /// /// The error message that explains the reason for the exception /// The exception that is the cause of the current exception public JsRuntimeException(string message, Exception innerException) - : this(message, string.Empty, string.Empty, innerException) - { } + : base(message, innerException) + { + Category = JsErrorCategory.Runtime; + } /// /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that is the cause of this exception /// /// The error message that explains the reason for the exception - /// Name of JavaScript engine - /// Version of original JavaScript engine + /// Name of JS engine + /// Version of original JS engine public JsRuntimeException(string message, string engineName, string engineVersion) - : this(message, engineName, engineVersion, null) - { } + : base(message, engineName, engineVersion) + { + Category = JsErrorCategory.Runtime; + } /// /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that is the cause of this exception /// /// The error message that explains the reason for the exception - /// Name of JavaScript engine - /// Version of original JavaScript engine + /// Name of JS engine + /// Version of original JS engine /// The exception that is the cause of the current exception public JsRuntimeException(string message, string engineName, string engineVersion, Exception innerException) : base(message, engineName, engineVersion, innerException) { - ErrorCode = string.Empty; - Category = string.Empty; - LineNumber = 0; - ColumnNumber = 0; - SourceFragment = string.Empty; + Category = JsErrorCategory.Runtime; + } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + protected JsRuntimeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info != null) + { + _callStack = info.GetString("CallStack"); + } } + + + #region JsException overrides + + /// + /// Populates a with the data needed to serialize the target object + /// + /// The to populate with data + /// The destination (see ) for this serialization + [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + base.GetObjectData(info, context); + info.AddValue("CallStack", _callStack); + } + + #endregion +#endif } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsScriptException.cs b/src/JavaScriptEngineSwitcher.Core/JsScriptException.cs new file mode 100644 index 00000000..d76bd655 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/JsScriptException.cs @@ -0,0 +1,176 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +using System.Security.Permissions; +#endif + +namespace JavaScriptEngineSwitcher.Core +{ + /// + /// The exception that is thrown during the script processing + /// +#if !NETSTANDARD1_3 + [Serializable] +#endif + public class JsScriptException : JsException + { + /// + /// Type of the script error + /// + private string _type = string.Empty; + + /// + /// Document name + /// + private string _documentName = string.Empty; + + /// + /// Line number + /// + private int _lineNumber; + + /// + /// Column number + /// + private int _columnNumber; + + /// + /// Source fragment + /// + private string _sourceFragment = string.Empty; + + /// + /// Gets or sets a type of the script error + /// + public string Type + { + get { return _type; } + set { _type = value; } + } + + /// + /// Gets or sets a document name + /// + public string DocumentName + { + get { return _documentName; } + set { _documentName = value; } + } + + /// + /// Gets or sets a line number + /// + public int LineNumber + { + get { return _lineNumber; } + set { _lineNumber = value; } + } + + /// + /// Gets or sets a column number + /// + public int ColumnNumber + { + get { return _columnNumber; } + set { _columnNumber = value; } + } + + /// + /// Gets or sets a source fragment + /// + public string SourceFragment + { + get { return _sourceFragment; } + set { _sourceFragment = value; } + } + + + /// + /// Initializes a new instance of the class + /// with a specified error message + /// + /// The message that describes the error + public JsScriptException(string message) + : base(message) + { } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception + /// that is the cause of this exception + /// + /// The error message that explains the reason for the exception + /// The exception that is the cause of the current exception + public JsScriptException(string message, Exception innerException) + : base(message, innerException) + { } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + public JsScriptException(string message, string engineName, string engineVersion) + : base(message, engineName, engineVersion) + { } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + /// The exception that is the cause of the current exception + public JsScriptException(string message, string engineName, string engineVersion, + Exception innerException) + : base(message, engineName, engineVersion, innerException) + { } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + protected JsScriptException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info != null) + { + _type = info.GetString("Type"); + _documentName = info.GetString("DocumentName"); + _lineNumber = info.GetInt32("LineNumber"); + _columnNumber = info.GetInt32("ColumnNumber"); + _sourceFragment = info.GetString("SourceFragment"); + } + } + + + #region JsException overrides + + /// + /// Populates a with the data needed to serialize the target object + /// + /// The to populate with data + /// The destination (see ) for this serialization + [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + base.GetObjectData(info, context); + info.AddValue("Type", _type); + info.AddValue("DocumentName", _documentName); + info.AddValue("LineNumber", _lineNumber); + info.AddValue("ColumnNumber", _columnNumber); + info.AddValue("SourceFragment", _sourceFragment); + } + + #endregion +#endif + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsTimeoutException.cs b/src/JavaScriptEngineSwitcher.Core/JsTimeoutException.cs new file mode 100644 index 00000000..fd016688 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/JsTimeoutException.cs @@ -0,0 +1,79 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using JavaScriptEngineSwitcher.Core.Constants; + +namespace JavaScriptEngineSwitcher.Core +{ + /// + /// The exception that is thrown when the time allotted for a script execution has expired + /// +#if !NETSTANDARD1_3 + [Serializable] +#endif + public sealed class JsTimeoutException : JsRuntimeException + { + /// + /// Initializes a new instance of the class + /// with a specified error message + /// + /// The message that describes the error + public JsTimeoutException(string message) + : base(message) + { + Category = JsErrorCategory.Timeout; + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception + /// that is the cause of this exception + /// + /// The error message that explains the reason for the exception + /// The exception that is the cause of the current exception + public JsTimeoutException(string message, Exception innerException) + : base(message, innerException) + { + Category = JsErrorCategory.Timeout; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + public JsTimeoutException(string message, string engineName, string engineVersion) + : base(message, engineName, engineVersion) + { + Category = JsErrorCategory.Timeout; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + /// The exception that is the cause of the current exception + public JsTimeoutException(string message, string engineName, string engineVersion, + Exception innerException) + : base(message, engineName, engineVersion, innerException) + { + Category = JsErrorCategory.Timeout; + } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + private JsTimeoutException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/JsUsageException.cs b/src/JavaScriptEngineSwitcher.Core/JsUsageException.cs new file mode 100644 index 00000000..2bc7eb84 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/JsUsageException.cs @@ -0,0 +1,79 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using JavaScriptEngineSwitcher.Core.Constants; + +namespace JavaScriptEngineSwitcher.Core +{ + /// + /// The API usage exception occurred + /// +#if !NETSTANDARD1_3 + [Serializable] +#endif + public sealed class JsUsageException : JsException + { + /// + /// Initializes a new instance of the class + /// with a specified error message + /// + /// The message that describes the error + public JsUsageException(string message) + : base(message) + { + Category = JsErrorCategory.Usage; + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception + /// that is the cause of this exception + /// + /// The error message that explains the reason for the exception + /// The exception that is the cause of the current exception + public JsUsageException(string message, Exception innerException) + : base(message, innerException) + { + Category = JsErrorCategory.Usage; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + public JsUsageException(string message, string engineName, string engineVersion) + : base(message, engineName, engineVersion) + { + Category = JsErrorCategory.Usage; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine + /// Version of original JS engine + /// The exception that is the cause of the current exception + public JsUsageException(string message, string engineName, string engineVersion, + Exception innerException) + : base(message, engineName, engineVersion, innerException) + { + Category = JsErrorCategory.Usage; + } +#if !NETSTANDARD1_3 + + /// + /// Initializes a new instance of the class with serialized data + /// + /// The object that holds the serialized data + /// The contextual information about the source or destination + private JsUsageException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/NotSupportedTypeException.cs b/src/JavaScriptEngineSwitcher.Core/NotSupportedTypeException.cs deleted file mode 100644 index 081f8f90..00000000 --- a/src/JavaScriptEngineSwitcher.Core/NotSupportedTypeException.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace JavaScriptEngineSwitcher.Core -{ - using System; - - /// - /// The exception that is thrown when a .NET type is not supported by JavaScipt engine - /// - public sealed class NotSupportedTypeException : Exception - { - /// - /// Initializes a new instance of the class - /// with a specified error message - /// - /// The message that describes the error - public NotSupportedTypeException(string message) - : base(message) - { } - - /// - /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that is the cause of this exception - /// - /// The error message that explains the reason for the exception - /// The exception that is the cause of the current exception - public NotSupportedTypeException(string message, Exception innerException) - : base(message, innerException) - { } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.Core/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..663374d6 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/PACKAGE-DESCRIPTION.md @@ -0,0 +1,2 @@ +JavaScript Engine Switcher determines unified interface for access to the basic features of popular JavaScript engines ([ChakraCore](https://github.com/chakra-core/ChakraCore), [Jint](https://github.com/sebastienros/jint), [Jurassic](https://github.com/paulbartrum/jurassic), [MSIE JavaScript Engine for .NET](https://github.com/Taritsyn/MsieJavaScriptEngine), [NiL.JS](https://github.com/nilproject/NiL.JS), [Jering.Javascript.NodeJS](https://github.com/JeringTech/Javascript.NodeJS), [Microsoft ClearScript.V8](https://github.com/Microsoft/ClearScript), [VroomJs](https://github.com/pauldotknopf/vroomjs-core) and [YantraJS](https://yantrajs.com/)). +This library allows you to quickly and easily switch to using of another JavaScript engine. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Polyfills/System/Reflection/TypeInfoExtensions.cs b/src/JavaScriptEngineSwitcher.Core/Polyfills/System/Reflection/TypeInfoExtensions.cs new file mode 100644 index 00000000..d74b00b7 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/Polyfills/System/Reflection/TypeInfoExtensions.cs @@ -0,0 +1,25 @@ +#if NETSTANDARD1_3 +using System; +using System.Reflection; + +namespace JavaScriptEngineSwitcher.Core.Polyfills.System.Reflection +{ + internal static class TypeInfoExtensions + { + public static bool IsInstanceOfType(this TypeInfo source, object o) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (o == null) + { + return false; + } + + return source.IsAssignableFrom(o.GetType().GetTypeInfo()); + } + } +} +#endif \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Properties/AssemblyInfo.cs b/src/JavaScriptEngineSwitcher.Core/Properties/AssemblyInfo.cs deleted file mode 100644 index 9ab33c86..00000000 --- a/src/JavaScriptEngineSwitcher.Core/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("JavaScriptEngineSwitcher.Core")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JavaScript Engine Switcher for .Net: Core")] -[assembly: AssemblyCopyright("Copyright © 2013-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("9f7e9fff-da85-4609-8bee-bdead5a3afe2")] - -[assembly: AssemblyVersion("1.5.0.0")] -[assembly: AssemblyFileVersion("1.5.0.0")] \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Resources/Strings.Designer.cs b/src/JavaScriptEngineSwitcher.Core/Resources/Strings.Designer.cs index bf32466d..8dc14700 100644 --- a/src/JavaScriptEngineSwitcher.Core/Resources/Strings.Designer.cs +++ b/src/JavaScriptEngineSwitcher.Core/Resources/Strings.Designer.cs @@ -1,381 +1,491 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 +// This code was generated by a tool. // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ +namespace JavaScriptEngineSwitcher.Core.Resources +{ + using System; + using System.Globalization; + using System.Reflection; + using System.Resources; + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + public class Strings + { + private static Lazy _resourceManager = + new Lazy(() => new ResourceManager( + "JavaScriptEngineSwitcher.Core.Resources.Strings", +#if NET20 || NET30 || NET35 || NET40 + typeof(Strings).Assembly +#else + typeof(Strings).GetTypeInfo().Assembly +#endif + )); + + private static CultureInfo _resourceCulture; + + /// + /// Returns a cached ResourceManager instance used by this class + /// + public static ResourceManager ResourceManager + { + get + { + return _resourceManager.Value; + } + } + + /// + /// Overrides a current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class + /// + public static CultureInfo Culture + { + get + { + return _resourceCulture; + } + set + { + _resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to "The parameter '{0}' must be a non-empty string." + /// + public static string Common_ArgumentIsEmpty + { + get { return GetString("Common_ArgumentIsEmpty"); } + } + + /// + /// Looks up a localized string similar to "The parameter '{0}' must be a non-nullable." + /// + public static string Common_ArgumentIsNull + { + get { return GetString("Common_ArgumentIsNull"); } + } + + /// + /// Looks up a localized string similar to "Cannot convert object of type `{0}` to type `{1}`." + /// + public static string Common_CannotConvertObjectToType + { + get { return GetString("Common_CannotConvertObjectToType"); } + } + + /// + /// Looks up a localized string similar to "Can not convert value '{0}' of enumeration type `{1}` to value of enumeration type `{2}`." + /// + public static string Common_EnumValueConversionFailed + { + get { return GetString("Common_EnumValueConversionFailed"); } + } + + /// + /// Looks up a localized string similar to "File '{0}' not exist." + /// + public static string Common_FileNotExist + { + get { return GetString("Common_FileNotExist"); } + } + + /// + /// Looks up a localized string similar to "Resource with name '{0}' is null." + /// + public static string Common_ResourceIsNull + { + get { return GetString("Common_ResourceIsNull"); } + } + + /// + /// Looks up a localized string similar to "See the original error message: “{0}”." + /// + public static string Common_SeeOriginalErrorMessage + { + get { return GetString("Common_SeeOriginalErrorMessage"); } + } + + /// + /// Looks up a localized string similar to "Value cannot be empty." + /// + public static string Common_ValueIsEmpty + { + get { return GetString("Common_ValueIsEmpty"); } + } + + /// + /// Looks up a localized string similar to "Value cannot be null." + /// + public static string Common_ValueIsNull + { + get { return GetString("Common_ValueIsNull"); } + } + + /// + /// Looks up a localized string similar to "Cannot convert null to a value type." + /// + public static string Common_ValueTypeCannotBeNull + { + get { return GetString("Common_ValueTypeCannotBeNull"); } + } + + /// + /// Looks up a localized string similar to "Assigning an instance of the JS engine switcher to the `Current` property of `JsEngineSwitcher`..." + /// + public static string Configuration_AssigningToJsEngineSwitcherCurrentPropertyForbidden + { + get { return GetString("Configuration_AssigningToJsEngineSwitcherCurrentPropertyForbidden"); } + } + + /// + /// Looks up a localized string similar to "Name of default JavaScript engine not specified." + /// + public static string Configuration_DefaultJsEngineNameNotSpecified + { + get { return GetString("Configuration_DefaultJsEngineNameNotSpecified"); } + } + + /// + /// Looks up a localized string similar to "Getting an instance of the JS engine switcher from the `Current` property of `JsEngineSwitcher`..." + /// + public static string Configuration_GettingFromJsEngineSwitcherCurrentPropertyForbidden + { + get { return GetString("Configuration_GettingFromJsEngineSwitcherCurrentPropertyForbidden"); } + } + + /// + /// Looks up a localized string similar to "Could not find a factory, that creates an instance of the JavaScript engine with name `{0}`." + /// + public static string Configuration_JsEngineFactoryNotFound + { + get { return GetString("Configuration_JsEngineFactoryNotFound"); } + } + + /// + /// Looks up a localized string similar to "Most likely it happened, because the '{0}' assembly or one of its dependencies was not found." + /// + public static string Engine_AssemblyNotFound + { + get { return GetString("Engine_AssemblyNotFound"); } + } + + /// + /// Looks up a localized string similar to "Failed to create instance of the {0}." + /// + public static string Engine_JsEngineNotLoaded + { + get { return GetString("Engine_JsEngineNotLoaded"); } + } + + /// + /// Looks up a localized string similar to "There is no {0} package for the {1} processor architecture." + /// + public static string Engine_NoNuGetPackageForProcessorArchitecture + { + get { return GetString("Engine_NoNuGetPackageForProcessorArchitecture"); } + } + + /// + /// Looks up a localized string similar to "Try to install the {0} package via NuGet." + /// + public static string Engine_NuGetPackageInstallationRequired + { + get { return GetString("Engine_NuGetPackageInstallationRequired"); } + } + + /// + /// Looks up a localized string similar to "Your operating system is not supported by the engine." + /// + public static string Engine_OperatingSystemNotSupported + { + get { return GetString("Engine_OperatingSystemNotSupported"); } + } + + /// + /// Looks up a localized string similar to "{0} processor architecture is not supported by the engine." + /// + public static string Engine_ProcessorArchitectureNotSupported + { + get { return GetString("Engine_ProcessorArchitectureNotSupported"); } + } + + /// + /// Looks up a localized string similar to "Call stack" + /// + public static string ErrorDetails_CallStack + { + get { return GetString("ErrorDetails_CallStack"); } + } + + /// + /// Looks up a localized string similar to "Category" + /// + public static string ErrorDetails_Category + { + get { return GetString("ErrorDetails_Category"); } + } + + /// + /// Looks up a localized string similar to "Column number" + /// + public static string ErrorDetails_ColumnNumber + { + get { return GetString("ErrorDetails_ColumnNumber"); } + } + + /// + /// Looks up a localized string similar to "Description" + /// + public static string ErrorDetails_Description + { + get { return GetString("ErrorDetails_Description"); } + } + + /// + /// Looks up a localized string similar to "Document name" + /// + public static string ErrorDetails_DocumentName + { + get { return GetString("ErrorDetails_DocumentName"); } + } + + /// + /// Looks up a localized string similar to "Engine name" + /// + public static string ErrorDetails_EngineName + { + get { return GetString("ErrorDetails_EngineName"); } + } + + /// + /// Looks up a localized string similar to "Engine version" + /// + public static string ErrorDetails_EngineVersion + { + get { return GetString("ErrorDetails_EngineVersion"); } + } + + /// + /// Looks up a localized string similar to "Line number" + /// + public static string ErrorDetails_LineNumber + { + get { return GetString("ErrorDetails_LineNumber"); } + } + + /// + /// Looks up a localized string similar to "Message" + /// + public static string ErrorDetails_Message + { + get { return GetString("ErrorDetails_Message"); } + } + + /// + /// Looks up a localized string similar to "Source fragment" + /// + public static string ErrorDetails_SourceFragment + { + get { return GetString("ErrorDetails_SourceFragment"); } + } + + /// + /// Looks up a localized string similar to "Type" + /// + public static string ErrorDetails_Type + { + get { return GetString("ErrorDetails_Type"); } + } + + /// + /// Looks up a localized string similar to "The function with the name '{0}' does not exist." + /// + public static string Runtime_FunctionNotExist + { + get { return GetString("Runtime_FunctionNotExist"); } + } + + /// + /// Looks up a localized string similar to "The '{0}' line of the script error location has an incorrect format." + /// + public static string Runtime_InvalidErrorLocationLineFormat + { + get { return GetString("Runtime_InvalidErrorLocationLineFormat"); } + } + + /// + /// Looks up a localized string similar to "Script execution was interrupted." + /// + public static string Runtime_ScriptInterrupted + { + get { return GetString("Runtime_ScriptInterrupted"); } + } + + /// + /// Looks up a localized string similar to "Script execution exceeded timeout." + /// + public static string Runtime_ScriptTimeoutExceeded + { + get { return GetString("Runtime_ScriptTimeoutExceeded"); } + } + + /// + /// Looks up a localized string similar to "The variable with the name '{0}' does not exist." + /// + public static string Runtime_VariableNotExist + { + get { return GetString("Runtime_VariableNotExist"); } + } + + /// + /// Looks up a localized string similar to "Cannot convert a pre-compiled script to internal type `{0}`." + /// + public static string Usage_CannotConvertPrecompiledScriptToInternalType + { + get { return GetString("Usage_CannotConvertPrecompiledScriptToInternalType"); } + } + + /// + /// Looks up a localized string similar to "Cannot execute a '{0}' file, because it is empty." + /// + public static string Usage_CannotExecuteEmptyFile + { + get { return GetString("Usage_CannotExecuteEmptyFile"); } + } + + /// + /// Looks up a localized string similar to "Cannot execute a '{0}' resource, because it is empty." + /// + public static string Usage_CannotExecuteEmptyResource + { + get { return GetString("Usage_CannotExecuteEmptyResource"); } + } + + /// + /// Looks up a localized string similar to "Cannot execute a pre-compiled script, because it was created for another JS engine with name `{0}`." + /// + public static string Usage_CannotExecutePrecompiledScriptForAnotherJsEngine + { + get { return GetString("Usage_CannotExecutePrecompiledScriptForAnotherJsEngine"); } + } + + /// + /// Looks up a localized string similar to "Cannot pre-compile a '{0}' file, because it is empty." + /// + public static string Usage_CannotPrecompileEmptyFile + { + get { return GetString("Usage_CannotPrecompileEmptyFile"); } + } + + /// + /// Looks up a localized string similar to "Cannot pre-compile a '{0}' resource, because it is empty." + /// + public static string Usage_CannotPrecompileEmptyResource + { + get { return GetString("Usage_CannotPrecompileEmptyResource"); } + } + + /// + /// Looks up a localized string similar to "The embedded host object '{0}' has a type `{1}`, which is not supported." + /// + public static string Usage_EmbeddedHostObjectTypeNotSupported + { + get { return GetString("Usage_EmbeddedHostObjectTypeNotSupported"); } + } + + /// + /// Looks up a localized string similar to "The embedded host type `{0}` is not supported." + /// + public static string Usage_EmbeddedHostTypeNotSupported + { + get { return GetString("Usage_EmbeddedHostTypeNotSupported"); } + } + + /// + /// Looks up a localized string similar to "One of the function parameters '{0}' has a type `{1}`, which is not supported." + /// + public static string Usage_FunctionParameterTypeNotSupported + { + get { return GetString("Usage_FunctionParameterTypeNotSupported"); } + } + + /// + /// Looks up a localized string similar to "The document name '{0}' has incorrect format." + /// + public static string Usage_InvalidDocumentNameFormat + { + get { return GetString("Usage_InvalidDocumentNameFormat"); } + } + + /// + /// Looks up a localized string similar to "The file name '{0}' has incorrect format." + /// + public static string Usage_InvalidFileNameFormat + { + get { return GetString("Usage_InvalidFileNameFormat"); } + } + + /// + /// Looks up a localized string similar to "The function name '{0}' has incorrect format." + /// + public static string Usage_InvalidFunctionNameFormat + { + get { return GetString("Usage_InvalidFunctionNameFormat"); } + } + + /// + /// Looks up a localized string similar to "The resource name '{0}' has incorrect format." + /// + public static string Usage_InvalidResourceNameFormat + { + get { return GetString("Usage_InvalidResourceNameFormat"); } + } + + /// + /// Looks up a localized string similar to "The script item name '{0}' has incorrect format." + /// + public static string Usage_InvalidScriptItemNameFormat + { + get { return GetString("Usage_InvalidScriptItemNameFormat"); } + } + + /// + /// Looks up a localized string similar to "The variable name '{0}' has incorrect format." + /// + public static string Usage_InvalidVariableNameFormat + { + get { return GetString("Usage_InvalidVariableNameFormat"); } + } + + /// + /// Looks up a localized string similar to "The type of return value `{0}` is not supported." + /// + public static string Usage_ReturnValueTypeNotSupported + { + get { return GetString("Usage_ReturnValueTypeNotSupported"); } + } + + /// + /// Looks up a localized string similar to "The variable '{0}' has a type `{1}`, which is not supported." + /// + public static string Usage_VariableTypeNotSupported + { + get { return GetString("Usage_VariableTypeNotSupported"); } + } + + private static string GetString(string name) + { + string value = ResourceManager.GetString(name, _resourceCulture); -namespace JavaScriptEngineSwitcher.Core.Resources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Strings { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Strings() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JavaScriptEngineSwitcher.Core.Resources.Strings", typeof(Strings).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to The parameter '{0}' must be a non-empty string.. - /// - public static string Common_ArgumentIsEmpty { - get { - return ResourceManager.GetString("Common_ArgumentIsEmpty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The parameter '{0}' must be a non-nullable.. - /// - public static string Common_ArgumentIsNull { - get { - return ResourceManager.GetString("Common_ArgumentIsNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to You do not specified a name of assembly.. - /// - public static string Common_AssemblyNameIsEmpty { - get { - return ResourceManager.GetString("Common_AssemblyNameIsEmpty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot convert object of type `{0}` to type `{1}`.. - /// - public static string Common_CannotConvertObjectToType { - get { - return ResourceManager.GetString("Common_CannotConvertObjectToType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Can not convert value '{0}' of enumeration type `{1}` to value of enumeration type `{2}`.. - /// - public static string Common_EnumValueConversionFailed { - get { - return ResourceManager.GetString("Common_EnumValueConversionFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to File '{0}' not exist.. - /// - public static string Common_FileNotExist { - get { - return ResourceManager.GetString("Common_FileNotExist", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During instantiate an object of type `{0}` from assembly `{1}` error occurred.. - /// - public static string Common_InstanceCreationFailed { - get { - return ResourceManager.GetString("Common_InstanceCreationFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to You do not specified a type name.. - /// - public static string Common_TypeNameIsEmpty { - get { - return ResourceManager.GetString("Common_TypeNameIsEmpty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Value cannot be empty.. - /// - public static string Common_ValueIsEmpty { - get { - return ResourceManager.GetString("Common_ValueIsEmpty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Value cannot be null.. - /// - public static string Common_ValueIsNull { - get { - return ResourceManager.GetString("Common_ValueIsNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot convert null to a value type.. - /// - public static string Common_ValueTypeCannotBeNull { - get { - return ResourceManager.GetString("Common_ValueTypeCannotBeNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Default JavaScript engine not specified.. - /// - public static string Configuration_DefaultJsEngineNotSpecified { - get { - return ResourceManager.GetString("Configuration_DefaultJsEngineNotSpecified", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to JavaScript engine with name `{0}` is not registered in configuration file.. - /// - public static string Configuration_JsEngineNotRegistered { - get { - return ResourceManager.GetString("Configuration_JsEngineNotRegistered", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Category. - /// - public static string ErrorDetails_Category { - get { - return ResourceManager.GetString("ErrorDetails_Category", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Column number. - /// - public static string ErrorDetails_ColumnNumber { - get { - return ResourceManager.GetString("ErrorDetails_ColumnNumber", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Engine name. - /// - public static string ErrorDetails_EngineName { - get { - return ResourceManager.GetString("ErrorDetails_EngineName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Engine version. - /// - public static string ErrorDetails_EngineVersion { - get { - return ResourceManager.GetString("ErrorDetails_EngineVersion", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Error code. - /// - public static string ErrorDetails_ErrorCode { - get { - return ResourceManager.GetString("ErrorDetails_ErrorCode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Line number. - /// - public static string ErrorDetails_LineNumber { - get { - return ResourceManager.GetString("ErrorDetails_LineNumber", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Message. - /// - public static string ErrorDetails_Message { - get { - return ResourceManager.GetString("ErrorDetails_Message", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Source fragment. - /// - public static string ErrorDetails_SourceFragment { - get { - return ResourceManager.GetString("ErrorDetails_SourceFragment", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Resource with name '{0}' is null.. - /// - public static string Resources_ResourceIsNull { - get { - return ResourceManager.GetString("Resources_ResourceIsNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The embedded host object '{0}' has a type `{1}`, which is not supported.. - /// - public static string Runtime_EmbeddedHostObjectTypeNotSupported { - get { - return ResourceManager.GetString("Runtime_EmbeddedHostObjectTypeNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The embedded host type `{0}` is not supported.. - /// - public static string Runtime_EmbeddedHostTypeNotSupported { - get { - return ResourceManager.GetString("Runtime_EmbeddedHostTypeNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The function name '{0}' is forbidden, as is contained in the list of reserved words of JavaScript language.. - /// - public static string Runtime_FunctionNameIsForbidden { - get { - return ResourceManager.GetString("Runtime_FunctionNameIsForbidden", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The function with the name '{0}' does not exist.. - /// - public static string Runtime_FunctionNotExist { - get { - return ResourceManager.GetString("Runtime_FunctionNotExist", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to One of the function parameters '{0}' has a type `{1}`, which is not supported.. - /// - public static string Runtime_FunctionParameterTypeNotSupported { - get { - return ResourceManager.GetString("Runtime_FunctionParameterTypeNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The function name '{0}' has incorrect format.. - /// - public static string Runtime_InvalidFunctionNameFormat { - get { - return ResourceManager.GetString("Runtime_InvalidFunctionNameFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The script item name '{0}' has incorrect format.. - /// - public static string Runtime_InvalidScriptItemNameFormat { - get { - return ResourceManager.GetString("Runtime_InvalidScriptItemNameFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The variable name '{0}' has incorrect format.. - /// - public static string Runtime_InvalidVariableNameFormat { - get { - return ResourceManager.GetString("Runtime_InvalidVariableNameFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to During loading of {0} error has occurred. - ///See more details: - /// - ///{1}. - /// - public static string Runtime_JsEngineNotLoaded { - get { - return ResourceManager.GetString("Runtime_JsEngineNotLoaded", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to load information about the method '{1}' of type `{0}`.. - /// - public static string Runtime_MethodInfoNotLoaded { - get { - return ResourceManager.GetString("Runtime_MethodInfoNotLoaded", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The type of return value `{0}` is not supported.. - /// - public static string Runtime_ReturnValueTypeNotSupported { - get { - return ResourceManager.GetString("Runtime_ReturnValueTypeNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The variable name '{0}' is forbidden, as is contained in the list of reserved words of JavaScript language.. - /// - public static string Runtime_VariableNameIsForbidden { - get { - return ResourceManager.GetString("Runtime_VariableNameIsForbidden", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The variable '{0}' has a type `{1}`, which is not supported.. - /// - public static string Runtime_VariableTypeNotSupported { - get { - return ResourceManager.GetString("Runtime_VariableTypeNotSupported", resourceCulture); - } - } - } -} + return value; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Resources/Strings.resx b/src/JavaScriptEngineSwitcher.Core/Resources/Strings.resx index e4ee47ef..d72925bf 100644 --- a/src/JavaScriptEngineSwitcher.Core/Resources/Strings.resx +++ b/src/JavaScriptEngineSwitcher.Core/Resources/Strings.resx @@ -123,9 +123,6 @@ The parameter '{0}' must be a non-nullable. - - You do not specified a name of assembly. - Cannot convert object of type `{0}` to type `{1}`. @@ -135,11 +132,11 @@ File '{0}' not exist. - - During instantiate an object of type `{0}` from assembly `{1}` error occurred. + + Resource with name '{0}' is null. - - You do not specified a type name. + + See the original error message: “{0}”. Value cannot be empty. @@ -150,11 +147,38 @@ Cannot convert null to a value type. - - Default JavaScript engine not specified. + + Assigning an instance of the JS engine switcher to the `Current` property of `JsEngineSwitcher` class is forbidden. To register an instance of the JS engine switcher, use the `IServiceCollection` interface or set the `AllowCurrentProperty` property of `JsEngineSwitcher` class to `true`. + + + Name of default JavaScript engine not specified. + + + Getting an instance of the JS engine switcher from the `Current` property of `JsEngineSwitcher` class is forbidden. To get an instance of the JS engine switcher, use the `IServiceCollection` interface or set the `AllowCurrentProperty` property of `JsEngineSwitcher` class to `true`. + + + Could not find a factory, that creates an instance of the JavaScript engine with name `{0}`. + + + Most likely it happened, because the '{0}' assembly or one of its dependencies was not found. + + + Failed to create instance of the {0}. + + + There is no {0} package for the {1} processor architecture. + + + Try to install the {0} package via NuGet. + + + Your operating system is not supported by the engine. - - JavaScript engine with name `{0}` is not registered in configuration file. + + {0} processor architecture is not supported by the engine. + + + Call stack Category @@ -162,15 +186,18 @@ Column number + + Description + + + Document name + Engine name Engine version - - Error code - Line number @@ -180,49 +207,73 @@ Source fragment - - Resource with name '{0}' is null. + + Type + + + The function with the name '{0}' does not exist. + + + The '{0}' line of the script error location has an incorrect format. - + + Script execution was interrupted. + + + Script execution exceeded timeout. + + + The variable with the name '{0}' does not exist. + + + Cannot convert a pre-compiled script to internal type `{0}`. + + + Cannot execute a '{0}' file, because it is empty. + + + Cannot execute a '{0}' resource, because it is empty. + + + Cannot execute a pre-compiled script, because it was created for another JS engine with name `{0}`. + + + Cannot pre-compile a '{0}' file, because it is empty. + + + Cannot pre-compile a '{0}' resource, because it is empty. + + The embedded host object '{0}' has a type `{1}`, which is not supported. - + The embedded host type `{0}` is not supported. - - The function name '{0}' is forbidden, as is contained in the list of reserved words of JavaScript language. + + One of the function parameters '{0}' has a type `{1}`, which is not supported. - - The function with the name '{0}' does not exist. + + The document name '{0}' has incorrect format. - - One of the function parameters '{0}' has a type `{1}`, which is not supported. + + The file name '{0}' has incorrect format. - + The function name '{0}' has incorrect format. - + + The resource name '{0}' has incorrect format. + + The script item name '{0}' has incorrect format. - + The variable name '{0}' has incorrect format. - - During loading of {0} error has occurred. -See more details: - -{1} - - - Failed to load information about the method '{1}' of type `{0}`. - - + The type of return value `{0}` is not supported. - - The variable name '{0}' is forbidden, as is contained in the list of reserved words of JavaScript language. - - + The variable '{0}' has a type `{1}`, which is not supported. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Resources/Strings.ru-ru.Designer.cs b/src/JavaScriptEngineSwitcher.Core/Resources/Strings.ru-RU.Designer.cs similarity index 100% rename from src/JavaScriptEngineSwitcher.Core/Resources/Strings.ru-ru.Designer.cs rename to src/JavaScriptEngineSwitcher.Core/Resources/Strings.ru-RU.Designer.cs diff --git a/src/JavaScriptEngineSwitcher.Core/Resources/Strings.ru-ru.resx b/src/JavaScriptEngineSwitcher.Core/Resources/Strings.ru-RU.resx similarity index 57% rename from src/JavaScriptEngineSwitcher.Core/Resources/Strings.ru-ru.resx rename to src/JavaScriptEngineSwitcher.Core/Resources/Strings.ru-RU.resx index 4ce03f5d..a51ccdf8 100644 --- a/src/JavaScriptEngineSwitcher.Core/Resources/Strings.ru-ru.resx +++ b/src/JavaScriptEngineSwitcher.Core/Resources/Strings.ru-RU.resx @@ -123,9 +123,6 @@ Параметр с именем "{0}" не должен содержать значение равное null! - - Вы не указали имя сборки! - Невозможно преобразовать объект типа `{0}` в тип `{1}`! @@ -135,11 +132,11 @@ Файл "{0}" не существует! - - При создании экземпляра объекта типа `{0}` из сборки `{1}` произошла ошибка! + + Ресурс с именем "{0}" содержит значение равное null! - - Вы не указали имя типа! + + Смотри оригинальное сообщение об ошибке: «{0}». Значение не должно быть пустым! @@ -150,11 +147,38 @@ Невозможно преобразовать null в значимый тип! - - Не указан JavaScript-движок, который должен использоваться по умолчанию! + + Присвоение экземпляра JS engine switcher свойству `Current` класса `JsEngineSwitcher` запрещено. Для регистрации экземпляра JS engine switcher используйте интерфейс `IServiceCollection` или присвойте свойству `AllowCurrentProperty` класса `JsEngineSwitcher` значение равное `true`. + + + Не указано имя JavaScript-движка, который должен использоваться по умолчанию! + + + Присвоение экземпляра JS engine switcher свойству `Current` класса `JsEngineSwitcher` запрещено. Для регистрации экземпляра JS engine switcher используйте интерфейс `IServiceCollection` или присвойте свойству `AllowCurrentProperty` класса `JsEngineSwitcher` значение равное `true`. + + + Не удалось найти фабрику, создающую экземпляр JavaScript-движка с именем `{0}`! + + + Скорее всего, это произошло, потому что не найдена сборка "{0}" или одна из ее зависимостей. + + + Не удалось создать экземпляр {0}. + + + Для процессорной архитектуры {1} не существует пакета {0}. + + + Попробуйте установить через NuGet пакет {0}. + + + Ваша операционная система не поддерживается движком. - - JavaScript-движок с именем `{0}` не зарегистрирован в конфигурационном файле! + + Процессорная архитектура {0} не поддерживается движком. + + + Стек вызовов Категория @@ -162,15 +186,18 @@ Номер столбца + + Описание + + + Имя документа + Название движка Версия движка - - Код ошибки - Номер строки @@ -180,49 +207,73 @@ Фрагмент исходного кода - - Ресурс с именем "{0}" содержит значение равное null! + + Тип + + + Функция с именем "{0}" не существует! + + + Строка местоположения ошибки "{0}" имеет некорректный формат! - + + Выполнение скрипта было прервано! + + + Превышено время ожидания выполнения скрипта! + + + Переменная с именем "{0}" не существует! + + + Невозможно преобразовать предварительно скомпилированный сценарий во внутренний тип `{0}`! + + + Нельзя выполнить файл "{0}", потому что он пустой! + + + Нельзя выполнить ресурс "{0}", потому что он пустой! + + + Нельзя выполнить предварительно скомпилированный сценарий, потому что он был создан для другого JS-движка с именем `{0}`! + + + Нельзя произвести предварительную компиляцию файла "{0}", потому что он пустой! + + + Нельзя произвести предварительную компиляцию ресурса "{0}", потому что он пустой! + + Встраиваемый объекта хоста "{0}" имеет тип `{1}`, который не поддерживается! - + Встраиваемый тип хоста `{0}` не поддерживается! - - Имя функции "{0}" запрещено, т.к. входит в список зарезервированных слов JavaScript! + + Один из параметров функции "{0}" имеет тип `{1}`, который не поддерживается! - - Функция с именем "{0}" не существует! + + Имя документа "{0}" имеет некорректный формат! - - Один из параметров функции "{0}" имеет тип `{1}`, который не поддерживается! + + Имя файла "{0}" имеет некорректный формат! - + Имя функции "{0}" имеет некорректный формат! - + + Имя ресурса "{0}" имеет некорректный формат! + + Имя скриптового элемента "{0}" имеет некорректный формат! - + Имя переменной "{0}" имеет некорректный формат! - - Во время загрузки {0} произошла ошибка! -Смотрите более подробную информацию об ошибке: - -{1} - - - Не удалось загрузить информацию о методе "{1}" типа `{0}`! - - + Тип возвращаемого значения `{0}` не поддерживается! - - Имя переменной "{0}" запрещено, т.к. входит в список зарезервированных слов JavaScript! - - + Переменная "{0}" имеет тип `{1}`, который не поддерживается! \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Undefined.cs b/src/JavaScriptEngineSwitcher.Core/Undefined.cs index cd4e9f34..0340b9e8 100644 --- a/src/JavaScriptEngineSwitcher.Core/Undefined.cs +++ b/src/JavaScriptEngineSwitcher.Core/Undefined.cs @@ -1,18 +1,20 @@ namespace JavaScriptEngineSwitcher.Core { /// - /// Represents an JavaScript undefined type + /// Represents an JS undefined type /// public sealed class Undefined { /// - /// Gets a one and only undefined instance + /// Gets a one and only undefined instance /// public static readonly Undefined Value = new Undefined(); + private Undefined() { } + /// /// Returns a string that represents the current object /// diff --git a/src/JavaScriptEngineSwitcher.Core/InterlockedStatedFlag.cs b/src/JavaScriptEngineSwitcher.Core/Utilities/InterlockedStatedFlag.cs similarity index 73% rename from src/JavaScriptEngineSwitcher.Core/InterlockedStatedFlag.cs rename to src/JavaScriptEngineSwitcher.Core/Utilities/InterlockedStatedFlag.cs index 54a3effd..41082d7c 100644 --- a/src/JavaScriptEngineSwitcher.Core/InterlockedStatedFlag.cs +++ b/src/JavaScriptEngineSwitcher.Core/Utilities/InterlockedStatedFlag.cs @@ -1,7 +1,7 @@ -namespace JavaScriptEngineSwitcher.Core -{ - using System.Threading; +using System.Threading; +namespace JavaScriptEngineSwitcher.Core.Utilities +{ public struct InterlockedStatedFlag { private int _counter; diff --git a/src/JavaScriptEngineSwitcher.Core/StatedFlag.cs b/src/JavaScriptEngineSwitcher.Core/Utilities/StatedFlag.cs similarity index 80% rename from src/JavaScriptEngineSwitcher.Core/StatedFlag.cs rename to src/JavaScriptEngineSwitcher.Core/Utilities/StatedFlag.cs index 54325ffd..df840b50 100644 --- a/src/JavaScriptEngineSwitcher.Core/StatedFlag.cs +++ b/src/JavaScriptEngineSwitcher.Core/Utilities/StatedFlag.cs @@ -1,4 +1,4 @@ -namespace JavaScriptEngineSwitcher.Core +namespace JavaScriptEngineSwitcher.Core.Utilities { public struct StatedFlag { diff --git a/src/JavaScriptEngineSwitcher.Core/Utilities/StringBuilderExtensions.cs b/src/JavaScriptEngineSwitcher.Core/Utilities/StringBuilderExtensions.cs deleted file mode 100644 index c76d6405..00000000 --- a/src/JavaScriptEngineSwitcher.Core/Utilities/StringBuilderExtensions.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace JavaScriptEngineSwitcher.Core.Utilities -{ - using System.Text; - using System.Text.RegularExpressions; - - /// - /// Extensions for StringBuilder - /// - internal static class StringBuilderExtensions - { - /// - /// Regular expression for format placeholder - /// - private static readonly Regex _formatPlaceholderRegExp = - new Regex(@"\{[0-9]+\}", RegexOptions.Multiline); - - /// - /// Appends the default line terminator to the end of the current System.Text.StringBuilder object - /// - /// Object StringBuilder - /// Object StringBuilder - public static StringBuilder AppendFormatLine(this StringBuilder sb) - { - return sb.AppendLine(); - } - - /// - /// Appends the string returned by processing a composite format string, which - /// contains zero or more format items, with default line terminator to this instance. - /// Each format item is replaced by the string representation of a corresponding - /// argument in a parameter array. - /// - /// Object StringBuilder - /// A composite format string - /// An array of objects to format - /// Object StringBuilder - public static StringBuilder AppendFormatLine(this StringBuilder sb, string format, params object[] args) - { - if (_formatPlaceholderRegExp.IsMatch(format)) - { - return sb.AppendFormat(format, args).AppendLine(); - } - - return sb.AppendLine(format.Replace("{{", "{").Replace("}}", "}")); - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Utilities/TypeConverter.cs b/src/JavaScriptEngineSwitcher.Core/Utilities/TypeConverter.cs index 8cb3465b..fcd47cd4 100644 --- a/src/JavaScriptEngineSwitcher.Core/Utilities/TypeConverter.cs +++ b/src/JavaScriptEngineSwitcher.Core/Utilities/TypeConverter.cs @@ -1,12 +1,18 @@ -namespace JavaScriptEngineSwitcher.Core.Utilities +using System; +using System.ComponentModel; +using System.Globalization; +#if NET45 || NETSTANDARD +using System.Reflection; +#endif +using OriginalTypeConverter = System.ComponentModel.TypeConverter; + +#if NETSTANDARD1_3 +using JavaScriptEngineSwitcher.Core.Polyfills.System.Reflection; +#endif +using JavaScriptEngineSwitcher.Core.Resources; + +namespace JavaScriptEngineSwitcher.Core.Utilities { - using System; - using System.ComponentModel; - using System.Globalization; - using OriginalTypeConverter = System.ComponentModel.TypeConverter; - - using Resources; - /// /// Type converter /// @@ -44,7 +50,7 @@ public static object ConvertToType(object value, Type targetType) /// The type to convert the value to /// The value to convert /// The value that has been converted to the target type - /// Result of conversion (true - success; false - failure) + /// Result of conversion (true - success; false - failure) public static bool TryConvertToType(object value, out T convertedValue) { object resultValue; @@ -62,7 +68,7 @@ public static bool TryConvertToType(object value, out T convertedValue) /// The value to convert /// The type to convert the value to /// The value that has been converted to the target type - /// Result of conversion (true - success; false - failure) + /// Result of conversion (true - success; false - failure) public static bool TryConvertToType(object value, Type targetType, out object convertedValue) { bool result = ConvertObjectToType(value, targetType, false, out convertedValue); @@ -102,9 +108,10 @@ private static bool ConvertObjectToType(object obj, Type type, bool throwOnError private static bool InnerConvertObjectToType(object obj, Type type, bool throwOnError, out object convertedObject) { + Type originalType = obj.GetType(); OriginalTypeConverter converter = TypeDescriptor.GetConverter(type); - if (converter.CanConvertFrom(obj.GetType())) + if (converter.CanConvertFrom(originalType)) { try { @@ -127,7 +134,7 @@ private static bool InnerConvertObjectToType(object obj, Type type, bool throwOn { try { - string text = TypeDescriptor.GetConverter(obj).ConvertToInvariantString(obj); + string text = TypeDescriptor.GetConverter(originalType).ConvertToInvariantString(obj); convertedObject = converter.ConvertFromInvariantString(text); return true; @@ -144,7 +151,11 @@ private static bool InnerConvertObjectToType(object obj, Type type, bool throwOn } } +#if NET40 if (type.IsInstanceOfType(obj)) +#else + if (type.GetTypeInfo().IsInstanceOfType(obj)) +#endif { convertedObject = obj; return true; @@ -153,7 +164,7 @@ private static bool InnerConvertObjectToType(object obj, Type type, bool throwOn if (throwOnError) { throw new InvalidOperationException( - string.Format(Strings.Common_CannotConvertObjectToType, obj.GetType(), type) + string.Format(Strings.Common_CannotConvertObjectToType, originalType, type) ); } @@ -163,14 +174,24 @@ private static bool InnerConvertObjectToType(object obj, Type type, bool throwOn private static bool IsNonNullableValueType(Type type) { - if (type == null || !type.IsValueType) + if (type == null) + { + return false; + } + +#if NET40 + Type typeInfo = type; +#else + TypeInfo typeInfo = type.GetTypeInfo(); +#endif + if (!typeInfo.IsValueType) { return false; } - if (type.IsGenericType) + if (typeInfo.IsGenericType) { - return (type.GetGenericTypeDefinition() != typeof(Nullable<>)); + return type.GetGenericTypeDefinition() != typeof(Nullable<>); } return true; diff --git a/src/JavaScriptEngineSwitcher.Core/Utilities/UniqueDocumentNameManager.cs b/src/JavaScriptEngineSwitcher.Core/Utilities/UniqueDocumentNameManager.cs new file mode 100644 index 00000000..248d7d00 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/Utilities/UniqueDocumentNameManager.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using JavaScriptEngineSwitcher.Core.Resources; + +namespace JavaScriptEngineSwitcher.Core.Utilities +{ + /// + /// Unique document name manager + /// + public sealed class UniqueDocumentNameManager + { + /// + /// Default document name + /// + private readonly string _defaultName; + + /// + /// Storage of unique names + /// + private readonly Dictionary _storage = new Dictionary(); + + /// + /// Synchronizer of unique name storage + /// + private readonly object _storageSynchronizer = new object(); + + + /// + /// Constructs an instance of the unique document name manager + /// + /// Default document name + public UniqueDocumentNameManager(string defaultName) + { + if (defaultName == null) + { + throw new ArgumentNullException( + nameof(defaultName), + string.Format(Strings.Common_ArgumentIsNull, nameof(defaultName)) + ); + } + + if (string.IsNullOrWhiteSpace(defaultName)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(defaultName)), + nameof(defaultName) + ); + } + + _defaultName = defaultName; + } + + + /// + /// Gets a unique document name + /// + /// Document name + /// Unique document name + public string GetUniqueName(string name) + { + string appropriateName = !string.IsNullOrWhiteSpace(name) ? + Path.GetFileNameWithoutExtension(name) : _defaultName; + string extension = Path.GetExtension(name); + + lock (_storageSynchronizer) + { + uint count; + _storage.TryGetValue(appropriateName, out count); + _storage[appropriateName] = ++count; + + string uniqueName = count > 1 ? + string.Concat(appropriateName, " [", count, "]", extension) + : + string.Concat(appropriateName, extension) + ; + + return uniqueName; + } + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Core/Utilities/Utils.cs b/src/JavaScriptEngineSwitcher.Core/Utilities/Utils.cs index 3b94dccd..f26cd11b 100644 --- a/src/JavaScriptEngineSwitcher.Core/Utilities/Utils.cs +++ b/src/JavaScriptEngineSwitcher.Core/Utilities/Utils.cs @@ -1,41 +1,146 @@ -namespace JavaScriptEngineSwitcher.Core.Utilities +using System; +#if NET40 +using System.Diagnostics; +#endif +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; + +using JavaScriptEngineSwitcher.Core.Resources; + +namespace JavaScriptEngineSwitcher.Core.Utilities { - using System; - using System.IO; - using System.Reflection; - using System.Text; - - using Resources; - public static class Utils { + /// + /// Flag indicating whether the current runtime is Mono + /// + private static readonly bool _isMonoRuntime; + + + /// + /// Static constructor + /// + static Utils() + { + _isMonoRuntime = Type.GetType("Mono.Runtime") != null; + } + + + /// + /// Determines whether the current runtime is Mono + /// + /// true if the runtime is Mono; otherwise, false + public static bool IsMonoRuntime() + { + return _isMonoRuntime; + } + + /// + /// Determines whether the current process is a 64-bit process + /// + /// true if the process is 64-bit; otherwise, false + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + public static bool Is64BitProcess() + { +#if NETSTANDARD1_3 + bool is64Bit = IntPtr.Size == 8; +#else + bool is64Bit = Environment.Is64BitProcess; +#endif + + return is64Bit; + } + /// /// Gets a content of the embedded resource as string /// - /// Resource name - /// Type from assembly that containing an embedded resource - /// Сontent of the embedded resource as string + /// The case-sensitive resource name without the namespace of the specified type + /// The type, that determines the assembly and whose namespace is used to scope + /// the resource name + /// Content of the embedded resource as string public static string GetResourceAsString(string resourceName, Type type) { + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(Strings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (type == null) + { + throw new ArgumentNullException( + nameof(type), + string.Format(Strings.Common_ArgumentIsNull, nameof(type)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + +#if NET40 Assembly assembly = type.Assembly; +#else + Assembly assembly = type.GetTypeInfo().Assembly; +#endif + string nameSpace = type.Namespace; + string resourceFullName = nameSpace != null ? nameSpace + "." + resourceName : resourceName; - return GetResourceAsString(resourceName, assembly); + return InnerGetResourceAsString(resourceFullName, assembly); } /// /// Gets a content of the embedded resource as string /// - /// Resource name - /// Assembly that containing an embedded resource - /// Сontent of the embedded resource as string + /// The case-sensitive resource name + /// The assembly, which contains the embedded resource + /// Content of the embedded resource as string public static string GetResourceAsString(string resourceName, Assembly assembly) + { + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(Strings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (assembly == null) + { + throw new ArgumentNullException( + nameof(assembly), + string.Format(Strings.Common_ArgumentIsNull, nameof(assembly)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(Strings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + return InnerGetResourceAsString(resourceName, assembly); + } + + private static string InnerGetResourceAsString(string resourceName, Assembly assembly) { using (Stream stream = assembly.GetManifestResourceStream(resourceName)) { if (stream == null) { throw new NullReferenceException( - string.Format(Strings.Resources_ResourceIsNull, resourceName)); + string.Format(Strings.Common_ResourceIsNull, resourceName) + ); } using (var reader = new StreamReader(stream)) @@ -46,7 +151,7 @@ public static string GetResourceAsString(string resourceName, Assembly assembly) } /// - /// Gets text content of the specified file + /// Gets a text content of the specified file /// /// File path /// Content encoding @@ -56,73 +161,24 @@ public static string GetFileTextContent(string path, Encoding encoding = null) if (!File.Exists(path)) { throw new FileNotFoundException( - string.Format(Strings.Common_FileNotExist, path), path); + string.Format(Strings.Common_FileNotExist, path), + path + ); } string content; - using (var file = new StreamReader(path, encoding ?? Encoding.UTF8)) + using (var stream = File.OpenRead(path)) + using (var reader = new StreamReader(stream, encoding ?? Encoding.UTF8)) { - content = file.ReadToEnd(); + content = reader.ReadToEnd(); } return content; } /// - /// Creates instance by specified full type name - /// - /// Full type name - /// Target type - /// Instance of type - internal static T CreateInstanceByFullTypeName(string fullTypeName) where T : class - { - if (string.IsNullOrWhiteSpace(fullTypeName)) - { - throw new ArgumentNullException(Strings.Common_ValueIsEmpty); - } - - string typeName; - string assemblyName; - Assembly assembly; - int commaPosition = fullTypeName.IndexOf(','); - - if (commaPosition != -1) - { - typeName = fullTypeName.Substring(0, commaPosition).Trim(); - if (string.IsNullOrEmpty(typeName)) - { - throw new EmptyValueException(Strings.Common_TypeNameIsEmpty); - } - - assemblyName = fullTypeName.Substring(commaPosition + 1, - fullTypeName.Length - (commaPosition + 1)).Trim(); - if (string.IsNullOrEmpty(assemblyName)) - { - throw new EmptyValueException(Strings.Common_AssemblyNameIsEmpty); - } - - assembly = Assembly.Load(assemblyName); - } - else - { - typeName = fullTypeName; - assembly = typeof(Utils).Assembly; - assemblyName = assembly.FullName; - } - - object instance = assembly.CreateInstance(typeName); - if (instance == null) - { - throw new NullReferenceException(string.Format(Strings.Common_InstanceCreationFailed, - typeName, assemblyName)); - } - - return (T)instance; - } - - /// - /// Converts value of source enumeration type to value of destination enumeration type + /// Converts a value of source enumeration type to value of destination enumeration type /// /// Source enumeration type /// Destination enumeration type diff --git a/src/JavaScriptEngineSwitcher.Core/readme.txt b/src/JavaScriptEngineSwitcher.Core/readme.txt new file mode 100644 index 00000000..cf5e35d8 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Core/readme.txt @@ -0,0 +1,24 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: Core v3.24.1 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + JavaScript Engine Switcher determines unified interface for access to the basic + features of popular JavaScript engines (ChakraCore, Jint, Jurassic, MSIE + JavaScript Engine for .NET, NiL.JS, Jering.Javascript.NodeJS, Microsoft + ClearScript.V8, VroomJs and YantraJS). This library allows you to quickly and + easily switch to using of another JavaScript engine. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection.csproj b/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection.csproj new file mode 100644 index 00000000..906de85a --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection.csproj @@ -0,0 +1,38 @@ + + + + JS Engine Switcher: MS Dependency Injection + 3.24.1 + net45;netstandard1.3;netstandard2.0 + 1.6.0 + Library + true + $(NoWarn);CS1591;NETSDK1215;NU1903 + false + true + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Extensions_MsDependencyInjection_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_Extensions_MsDependencyInjection_Logo128x128.png + JavaScriptEngineSwitcher.Extensions.MsDependencyInjection contains extension methods for adding the JS engine switcher in an `IServiceCollection`. + $(PackageCommonTags);DI + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/JsEngineSwitcherOptions.cs b/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/JsEngineSwitcherOptions.cs new file mode 100644 index 00000000..f22887a3 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/JsEngineSwitcherOptions.cs @@ -0,0 +1,42 @@ +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Extensions.MsDependencyInjection +{ + /// + /// Options of the JS engine switcher + /// + public sealed class JsEngineSwitcherOptions + { + /// + /// Gets or sets a flag for whether to allow usage of the property + /// + /// + /// Required to ensure the usage of an instance of the JS engine switcher that is registered by using + /// the IServiceCollection interface. + /// + public bool AllowCurrentProperty + { + get; + set; + } + + /// + /// Gets or sets a name of default JS engine + /// + public string DefaultEngineName + { + get; + set; + } + + + /// + /// Constructs an instance of the JS engine switcher options + /// + public JsEngineSwitcherOptions() + { + AllowCurrentProperty = true; + DefaultEngineName = string.Empty; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/JsEngineSwitcherServiceCollectionExtensions.cs b/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/JsEngineSwitcherServiceCollectionExtensions.cs new file mode 100644 index 00000000..624b2f76 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/JsEngineSwitcherServiceCollectionExtensions.cs @@ -0,0 +1,194 @@ +using System; + +using Microsoft.Extensions.DependencyInjection; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Extensions.MsDependencyInjection +{ + /// + /// Extension methods for adding the JS engine switcher in an + /// + public static class JsEngineSwitcherServiceCollectionExtensions + { + /// + /// Adds a default instance of the JS engine switcher to the + /// + /// The services available in the application + /// Instance of the + public static JsEngineFactoryCollection AddJsEngineSwitcher(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + var options = new JsEngineSwitcherOptions(); + + IJsEngineSwitcher engineSwitcher = CreateJsEngineSwitcher(options); + ApplyOptionsToJsEngineSwitcher(engineSwitcher, options); + RegisterJsEngineSwitcher(services, engineSwitcher, options); + + return engineSwitcher.EngineFactories; + } + + /// + /// Adds a specified instance of the JS engine switcher to the + /// + /// The services available in the application + /// Instance of the JS engine switcher + /// Instance of the + public static JsEngineFactoryCollection AddJsEngineSwitcher(this IServiceCollection services, + IJsEngineSwitcher engineSwitcher) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (engineSwitcher == null) + { + throw new ArgumentNullException(nameof(engineSwitcher)); + } + + var options = new JsEngineSwitcherOptions(); + + IJsEngineSwitcher currentEngineSwitcher = GetJsEngineSwitcher(engineSwitcher, options); + ApplyOptionsToJsEngineSwitcher(currentEngineSwitcher, options); + RegisterJsEngineSwitcher(services, currentEngineSwitcher, options); + + return currentEngineSwitcher.EngineFactories; + } + + /// + /// Adds a default instance of the JS engine switcher to the + /// + /// The services available in the application + /// The which need to be configured + /// Instance of the + public static JsEngineFactoryCollection AddJsEngineSwitcher(this IServiceCollection services, + Action configure) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var options = new JsEngineSwitcherOptions(); + configure(options); + + IJsEngineSwitcher engineSwitcher = CreateJsEngineSwitcher(options); + ApplyOptionsToJsEngineSwitcher(engineSwitcher, options); + RegisterJsEngineSwitcher(services, engineSwitcher, options); + + return engineSwitcher.EngineFactories; + } + + /// + /// Adds a specified instance of the JS engine switcher to + /// + /// The services available in the application + /// Instance of the JS engine switcher + /// The which need to be configured + /// Instance of the + public static JsEngineFactoryCollection AddJsEngineSwitcher(this IServiceCollection services, + IJsEngineSwitcher engineSwitcher, Action configure) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (engineSwitcher == null) + { + throw new ArgumentNullException(nameof(engineSwitcher)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var options = new JsEngineSwitcherOptions(); + configure(options); + + IJsEngineSwitcher currentEngineSwitcher = GetJsEngineSwitcher(engineSwitcher, options); + ApplyOptionsToJsEngineSwitcher(currentEngineSwitcher, options); + RegisterJsEngineSwitcher(services, currentEngineSwitcher, options); + + return currentEngineSwitcher.EngineFactories; + } + + #region Helper methods + + /// + /// Creates an instance of the JS engine switcher + /// + /// Options of the JS engine switcher + /// Instance of the JS engine switcher + private static IJsEngineSwitcher CreateJsEngineSwitcher(JsEngineSwitcherOptions options) + { + IJsEngineSwitcher engineSwitcher = options.AllowCurrentProperty ? + JsEngineSwitcher.Current + : + new JsEngineSwitcher() + ; + + return engineSwitcher; + } + + /// + /// Gets a instance of the JS engine switcher + /// + /// Instance of the JS engine switcher + /// Options of the JS engine switcher + /// Current instance of the JS engine switcher + private static IJsEngineSwitcher GetJsEngineSwitcher(IJsEngineSwitcher engineSwitcher, JsEngineSwitcherOptions options) + { + IJsEngineSwitcher currentEngineSwitcher = options.AllowCurrentProperty ? + JsEngineSwitcher.Current + : + engineSwitcher + ; + + return currentEngineSwitcher; + } + + /// + /// Applies a options to the JS engine switcher + /// + /// Instance of the JS engine switcher + /// Options of the JS engine switcher + private static void ApplyOptionsToJsEngineSwitcher(IJsEngineSwitcher engineSwitcher, JsEngineSwitcherOptions options) + { + JsEngineSwitcher.AllowCurrentProperty = options.AllowCurrentProperty; + engineSwitcher.DefaultEngineName = options.DefaultEngineName; + } + + /// + /// Registers a instance of the JS engine switcher + /// + /// The services available in the application + /// Instance of the JS engine switcher + /// Options of the JS engine switcher + private static void RegisterJsEngineSwitcher(IServiceCollection services, IJsEngineSwitcher engineSwitcher, + JsEngineSwitcherOptions options) + { + // Register the current instance of JS engine switcher as a service + services.AddSingleton(engineSwitcher); + + // Set the current instance of JS engine switcher + if (options.AllowCurrentProperty) + { + JsEngineSwitcher.Current = engineSwitcher; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..95b60008 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/PACKAGE-DESCRIPTION.md @@ -0,0 +1 @@ +JavaScriptEngineSwitcher.Extensions.MsDependencyInjection contains extension methods for adding the JS engine switcher in an `IServiceCollection`. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/readme.txt b/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/readme.txt new file mode 100644 index 00000000..a4b6267d --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Extensions.MsDependencyInjection/readme.txt @@ -0,0 +1,21 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: MS Dependency Injection v3.24.1 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + JavaScriptEngineSwitcher.Extensions.MsDependencyInjection contains extension + methods for adding the JS engine switcher in an `IServiceCollection`. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/Configuration/JintConfiguration.cs b/src/JavaScriptEngineSwitcher.Jint/Configuration/JintConfiguration.cs deleted file mode 100644 index 48c84668..00000000 --- a/src/JavaScriptEngineSwitcher.Jint/Configuration/JintConfiguration.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace JavaScriptEngineSwitcher.Jint.Configuration -{ - using System.Configuration; - - /// - /// Configuration settings of Jint JavaScript engine - /// - public sealed class JintConfiguration : ConfigurationSection - { - /// - /// Gets or sets a flag for whether to allow the debugger statement - /// to be called in a script - /// - [ConfigurationProperty("allowDebuggerStatement", DefaultValue = false)] - public bool AllowDebuggerStatement - { - get { return (bool)this["allowDebuggerStatement"]; } - set { this["allowDebuggerStatement"] = value; } - } - - /// - /// Gets or sets a flag for whether to enable debug mode - /// - [ConfigurationProperty("enableDebugging", DefaultValue = false)] - public bool EnableDebugging - { - get { return (bool)this["enableDebugging"]; } - set { this["enableDebugging"] = value; } - } - - /// - /// Gets or sets a maximum allowed depth of recursion: - /// -1 - recursion without limits; - /// N - one scope function can be called no more than N times. - /// - [ConfigurationProperty("maxRecursionDepth", DefaultValue = -1)] - [IntegerValidator(MinValue = -1, MaxValue = int.MaxValue, ExcludeRange = false)] - public int MaxRecursionDepth - { - get { return (int)this["maxRecursionDepth"]; } - set { this["maxRecursionDepth"] = value; } - } - - /// - /// Gets or sets a maximum number of statements - /// - [ConfigurationProperty("maxStatements", DefaultValue = 0)] - [IntegerValidator(MinValue = 0, MaxValue = int.MaxValue, ExcludeRange = false)] - public int MaxStatements - { - get { return (int)this["maxStatements"]; } - set { this["maxStatements"] = value; } - } - - /// - /// Gets or sets a flag for whether to allow run the script in strict mode - /// - [ConfigurationProperty("strictMode", DefaultValue = false)] - public bool StrictMode - { - get { return (bool)this["strictMode"]; } - set { this["strictMode"] = value; } - } - - /// - /// Gets or sets a number of milliseconds to wait before the script execution times out - /// - [ConfigurationProperty("timeout", DefaultValue = 0)] - public int Timeout - { - get { return (int)this["timeout"]; } - set { this["timeout"] = value; } - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/CustomTypeResolvers.cs b/src/JavaScriptEngineSwitcher.Jint/CustomTypeResolvers.cs new file mode 100644 index 00000000..4e1dfd8a --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jint/CustomTypeResolvers.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; + +using OriginalTypeResolver = Jint.Runtime.Interop.TypeResolver; + +namespace JavaScriptEngineSwitcher.Jint +{ + /// + /// Interop strategies for different values of the configuration property + /// + internal static class CustomTypeResolvers + { + private static readonly PropertyInfo[] _disallowedProperties = + { + typeof(Delegate).GetProperty("Method"), + typeof(Exception).GetProperty("TargetSite") + }; + + private static readonly MethodInfo[] _disallowedMethods = + { + typeof(object).GetMethod("GetType"), + typeof(Exception).GetMethod("GetType") + }; + + private static readonly Lazy _allowingReflection = new Lazy( + () => new OriginalTypeResolver() { MemberFilter = _ => true }); + + private static readonly Lazy _disallowingReflection = new Lazy( + () => new OriginalTypeResolver() { MemberFilter = IsAllowedMember }); + + /// + /// Gets a interop strategy that allows the usage of reflection API in the script code + /// + public static OriginalTypeResolver AllowingReflection => _allowingReflection.Value; + + /// + /// Gets a interop strategy that disallows the usage of reflection API in the script code + /// + public static OriginalTypeResolver DisallowingReflection => _disallowingReflection.Value; + + + private static bool IsAllowedMember(MemberInfo member) + { + bool isAllowed = true; + + if (member is PropertyInfo) + { + isAllowed = IsAllowedProperty((PropertyInfo)member); + } + else if (member is MethodInfo) + { + isAllowed = IsAllowedMethod((MethodInfo)member); + } + + return isAllowed; + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static bool IsAllowedProperty(PropertyInfo property) + { + bool isAllowed = !_disallowedProperties.Contains(property, MemberComparer.Instance); + + return isAllowed; + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static bool IsAllowedMethod(MethodInfo method) + { + bool isAllowed = !_disallowedMethods.Contains(method, MemberComparer.Instance); + + return isAllowed; + } + + + private sealed class MemberComparer : EqualityComparer + where T : MemberInfo + { + public static MemberComparer Instance { get; } = new MemberComparer(); + + + private MemberComparer() + { } + + + #region MemberComparer overrides + + public override bool Equals(T x, T y) + { + if (x == null && y == null) + { + return true; + } + else if (x == null || y == null) + { + return false; + } + + return x.Module == y.Module && x.MetadataToken == y.MetadataToken; + } + + public override int GetHashCode(T obj) + { + return obj != null ? obj.GetHashCode() : 0; + } + + #endregion + } + } +} diff --git a/src/JavaScriptEngineSwitcher.Jint/Helpers/JintJsErrorHelpers.cs b/src/JavaScriptEngineSwitcher.Jint/Helpers/JintJsErrorHelpers.cs new file mode 100644 index 00000000..af37c9df --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jint/Helpers/JintJsErrorHelpers.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Text.RegularExpressions; + +using AdvancedStringBuilder; + +using JavaScriptEngineSwitcher.Core.Extensions; +using JavaScriptEngineSwitcher.Core.Helpers; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; + +namespace JavaScriptEngineSwitcher.Jint.Helpers +{ + /// + /// JS error helpers + /// + internal static class JintJsErrorHelpers + { + #region Error location + + private const string OriginalAnonymousFunctionName = "(anonymous)"; + private const string WrapperAnonymousFunctionName = "Anonymous function"; + private const string DelegateFunctionName = "delegate"; + + /// + /// Pattern for working with document names with coordinates + /// + private static readonly string DocumentNameWithCoordinatesPattern = + @"(?" + CommonRegExps.DocumentNamePattern + @"):" + + @"(?\d+)(?::(?\d+))?"; + + /// + /// Regular expression for working with line of the script error location + /// + private static readonly Regex _errorLocationLineRegex = + new Regex(@"^[ ]{4}at " + + @"(?:" + + @"(?" + + @"[\w][\w ]*" + + @"|" + + CommonRegExps.JsFullNamePattern + + @"|" + + Regex.Escape(OriginalAnonymousFunctionName) + + @") " + + @"\(" + DocumentNameWithCoordinatesPattern + @"\)" + + @"|" + + DocumentNameWithCoordinatesPattern + + @")" + + "$"); + + /// + /// Regular expression for working with the syntax error message + /// + private static readonly Regex _syntaxErrorMessageRegex = + new Regex(@"^(?[\s\S]+?) \(" + CommonRegExps.DocumentNamePattern + @":\d+:\d+\)$"); + + /// + /// Regular expression for working with the script preparation error message + /// + private static readonly Regex _scriptPreparationErrorMessageRegex = + new Regex(@"^Could not prepare script: (?[\s\S]+?) " + + @"\((?" + CommonRegExps.DocumentNamePattern + @"):" + + @"(?\d+):(?\d+)\)$"); + + + /// + /// Parses a string representation of the script error location to produce an array of + /// instances + /// + /// String representation of the script error location + /// An array of instances + public static ErrorLocationItem[] ParseErrorLocation(string errorLocation) + { + if (string.IsNullOrWhiteSpace(errorLocation)) + { + return new ErrorLocationItem[0]; + } + + var errorLocationItems = new List(); + string[] lines = errorLocation.SplitToLines(); + int lineCount = lines.Length; + + for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) + { + string line = lines[lineIndex]; + Match lineMatch = _errorLocationLineRegex.Match(line); + + if (lineMatch.Success) + { + GroupCollection lineGroups = lineMatch.Groups; + + var errorLocationItem = new ErrorLocationItem + { + FunctionName = lineGroups["functionName"].Value, + DocumentName = lineGroups["documentName"].Value, + LineNumber = int.Parse(lineGroups["lineNumber"].Value), + ColumnNumber = lineGroups["columnNumber"].Success ? + int.Parse(lineGroups["columnNumber"].Value) : 0 + }; + errorLocationItems.Add(errorLocationItem); + } + else + { + Debug.WriteLine(string.Format(CoreStrings.Runtime_InvalidErrorLocationLineFormat, line)); + return new ErrorLocationItem[0]; + } + } + + return errorLocationItems.ToArray(); + } + + /// + /// Fixes a error location items + /// + /// An array of instances + public static void FixErrorLocationItems(ErrorLocationItem[] errorLocationItems) + { + foreach (ErrorLocationItem errorLocationItem in errorLocationItems) + { + string functionName = errorLocationItem.FunctionName; + if (functionName.Length == 0 || functionName == DelegateFunctionName) + { + errorLocationItem.FunctionName = "Global code"; + } + else if (functionName == OriginalAnonymousFunctionName) + { + errorLocationItem.FunctionName = WrapperAnonymousFunctionName; + } + } + } + + /// + /// Converts a call chain to stack + /// + /// Call chain + /// Call stack + public static string ConvertCallChainToStack(string callChain) + { + string callStack = string.Empty; + string[] callChainItems = callChain + .Split(new string[] { "->" }, StringSplitOptions.None) + ; + + if (callChainItems.Length > 0) + { + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder stackBuilder = stringBuilderPool.Rent(); + + for (int chainItemIndex = callChainItems.Length - 1; chainItemIndex >= 0; chainItemIndex--) + { + string chainItem = callChainItems[chainItemIndex]; + if (chainItem == OriginalAnonymousFunctionName) + { + chainItem = WrapperAnonymousFunctionName; + } + + JsErrorHelpers.WriteErrorLocationLine(stackBuilder, chainItem, string.Empty, 0, 0); + if (chainItemIndex > 0) + { + stackBuilder.AppendLine(); + } + } + + callStack = stackBuilder.ToString(); + stringBuilderPool.Return(stackBuilder); + } + + return callStack; + } + + /// + /// Gets a description from the syntax error message + /// + /// Error message + /// Description of error + public static string GetDescriptionFromSyntaxErrorMessage(string message) + { + Match messageMatch = _syntaxErrorMessageRegex.Match(message); + string description = messageMatch.Success ? + messageMatch.Groups["description"].Value : message; + + return description; + } + + /// + /// Parses a script preparation error message + /// + /// Error message + /// Description of error + /// Script error location + public static void ParseScriptPreparationErrorMessage(string message, out string description, + out ErrorLocationItem errorLocation) + { + Match messageMatch = _scriptPreparationErrorMessageRegex.Match(message); + + if (messageMatch.Success) + { + GroupCollection messageGroups = messageMatch.Groups; + + description = messageGroups["description"].Value; + errorLocation = new ErrorLocationItem + { + DocumentName = messageGroups["documentName"].Value, + LineNumber = int.Parse(messageGroups["lineNumber"].Value), + ColumnNumber = int.Parse(messageGroups["columnNumber"].Value) + }; + } + else + { + description = message; + errorLocation = new ErrorLocationItem(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/JavaScriptEngineSwitcher.Jint.csproj b/src/JavaScriptEngineSwitcher.Jint/JavaScriptEngineSwitcher.Jint.csproj index 716abdb1..600738b9 100644 --- a/src/JavaScriptEngineSwitcher.Jint/JavaScriptEngineSwitcher.Jint.csproj +++ b/src/JavaScriptEngineSwitcher.Jint/JavaScriptEngineSwitcher.Jint.csproj @@ -1,95 +1,38 @@ - - - + + - Debug - AnyCPU - {26ECE52E-991F-4E12-BB88-467201038EFF} + JS Engine Switcher: Jint + 3.30.2 + net462;netstandard2.0;netstandard2.1;net8.0 Library - Properties - JavaScriptEngineSwitcher.Jint - JavaScriptEngineSwitcher.Jint - v4.0 - 512 - ..\..\ - Client - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + true + $(NoWarn);CS1591;NU5104 + false + true + true + + + + + + - true + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Jint_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_Jint_Logo128x128.png + JavaScriptEngineSwitcher.Jint contains a `JintJsEngine` adapter (wrapper for the Jint). + $(PackageCommonTags);Jint + Jint was updated to version 4.4.2. - - ..\..\JavaScriptEngineSwitcher.snk - - - - {5c903eef-bad1-43b8-bfe2-e4ee4d204410} - JavaScriptEngineSwitcher.Core - - - - - False - ..\..\Binaries\Jint\Jint.dll - - - - - - - - - - - - Strings.ru-ru.resx - True - True - - - True - True - Strings.resx - - + - - JavaScriptEngineSwitcher.snk - + + + + - - ResXFileCodeGenerator - Strings.ru-ru.Designer.cs - - - ResXFileCodeGenerator - Strings.Designer.cs - + - - - + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/JintJsEngine.cs b/src/JavaScriptEngineSwitcher.Jint/JintJsEngine.cs index 42325018..dd1f80ab 100644 --- a/src/JavaScriptEngineSwitcher.Jint/JintJsEngine.cs +++ b/src/JavaScriptEngineSwitcher.Jint/JintJsEngine.cs @@ -1,260 +1,413 @@ -using IOriginalCallable = Jint.Native.ICallable; -using OriginalJsEngine = Jint.Engine; -using OriginalJsException = Jint.Runtime.JavaScriptException; -using OriginalJsValue = Jint.Native.JsValue; +using System; +using System.Threading; + +using Jint; +using IOriginalPrimitive = Jint.Native.IJsPrimitive; +using OriginalCancellationConstraint = Jint.Constraints.CancellationConstraint; +using OriginalDebuggerEventHandler = Jint.Runtime.Debugger.DebugHandler.DebugEventHandler; +using OriginalDebuggerStatementHandlingMode = Jint.Runtime.Debugger.DebuggerStatementHandling; +using OriginalEngine = Jint.Engine; +using OriginalErrorPosition = Acornima.Position; +using OriginalExecutionCanceledException = Jint.Runtime.ExecutionCanceledException; +using OriginalException = Jint.JintException; +using OriginalJavaScriptException = Jint.Runtime.JavaScriptException; +using OriginalMemoryLimitExceededException = Jint.Runtime.MemoryLimitExceededException; using OriginalObjectInstance = Jint.Native.Object.ObjectInstance; -using OriginalParserException = Jint.Parser.ParserException; +using OriginalParsedScript = Jint.Prepared; using OriginalRecursionDepthOverflowException = Jint.Runtime.RecursionDepthOverflowException; +using OriginalScriptPreparationException = Jint.ScriptPreparationException; using OriginalStatementsCountOverflowException = Jint.Runtime.StatementsCountOverflowException; using OriginalTypeReference = Jint.Runtime.Interop.TypeReference; +using OriginalTypes = Jint.Runtime.Types; +using OriginalValue = Jint.Native.JsValue; -namespace JavaScriptEngineSwitcher.Jint -{ - using System; +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Constants; +using JavaScriptEngineSwitcher.Core.Helpers; +using JavaScriptEngineSwitcher.Core.Utilities; - using Core; - using Core.Utilities; - using CoreStrings = Core.Resources.Strings; +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; +using WrapperCompilationException = JavaScriptEngineSwitcher.Core.JsCompilationException; +using WrapperInterruptedException = JavaScriptEngineSwitcher.Core.JsInterruptedException; +using WrapperRuntimeException = JavaScriptEngineSwitcher.Core.JsRuntimeException; +using WrapperScriptException = JavaScriptEngineSwitcher.Core.JsScriptException; +using WrapperTimeoutException = JavaScriptEngineSwitcher.Core.JsTimeoutException; +using WrapperUsageException = JavaScriptEngineSwitcher.Core.JsUsageException; - using Configuration; - using Resources; +using JavaScriptEngineSwitcher.Jint.Helpers; +namespace JavaScriptEngineSwitcher.Jint +{ /// - /// Adapter for Jint JavaScript engine + /// Adapter for the Jint JS engine /// public sealed class JintJsEngine : JsEngineBase { /// - /// Name of JavaScript engine + /// Name of JS engine /// - private const string ENGINE_NAME = "Jint JavaScript engine"; + public const string EngineName = "JintJsEngine"; /// - /// Version of original JavaScript engine + /// Version of original JS engine /// - private const string ENGINE_VERSION = "May 12, 2016"; + private const string EngineVersion = "4.4.2"; /// /// Jint JS engine /// - private OriginalJsEngine _jsEngine; + private OriginalEngine _jsEngine; /// - /// Synchronizer of code execution + /// Token source for canceling of script execution /// - private readonly object _executionSynchronizer = new object(); + private CancellationTokenSource _cancellationTokenSource; /// - /// Gets a name of JavaScript engine + /// Constraint for canceling of script execution /// - public override string Name - { - get { return ENGINE_NAME; } - } + private OriginalCancellationConstraint _cancellationConstraint; /// - /// Gets a version of original JavaScript engine + /// Debugger break callback /// - public override string Version - { - get { return ENGINE_VERSION; } - } + private OriginalDebuggerEventHandler _debuggerBreakCallback; + + /// + /// Debugger step callback + /// + private OriginalDebuggerEventHandler _debuggerStepCallback; + + /// + /// Flag for whether to allow run the script in strict mode + /// + private bool _strictMode; + + /// + /// Synchronizer of script execution + /// + private readonly object _executionSynchronizer = new object(); + + /// + /// Unique document name manager + /// + private readonly UniqueDocumentNameManager _documentNameManager = + new UniqueDocumentNameManager(DefaultDocumentName); /// - /// Constructs a instance of adapter for Jint + /// Constructs an instance of adapter for the Jint JS engine /// public JintJsEngine() - : this(JsEngineSwitcher.Current.GetJintConfiguration()) + : this(new JintSettings()) { } /// - /// Constructs a instance of adapter for Jint + /// Constructs an instance of adapter for the Jint JS engine /// - /// Configuration settings of Jint JavaScript engine - public JintJsEngine(JintConfiguration config) + /// Settings of the Jint JS engine + public JintJsEngine(JintSettings settings) { - JintConfiguration jintConfig = config ?? new JintConfiguration(); + _cancellationTokenSource = new CancellationTokenSource(); + + JintSettings jintSettings = settings ?? new JintSettings(); + _debuggerBreakCallback = jintSettings.DebuggerBreakCallback; + _debuggerStepCallback = jintSettings.DebuggerStepCallback; + var debuggerStatementHandlingMode = Utils.GetEnumFromOtherEnum( + jintSettings.DebuggerStatementHandlingMode); try { - _jsEngine = new OriginalJsEngine(c => c - .AllowDebuggerStatement(jintConfig.AllowDebuggerStatement) - .DebugMode(jintConfig.EnableDebugging) - .LimitRecursion(jintConfig.MaxRecursionDepth) - .MaxStatements(jintConfig.MaxStatements) - .Strict(jintConfig.StrictMode) - .TimeoutInterval(TimeSpan.FromMilliseconds(jintConfig.Timeout)) - ); + _jsEngine = new OriginalEngine(options => { + options.Interop.AllowGetType = true; + options.Interop.AllowSystemReflection = true; + + options + .CancellationToken(_cancellationTokenSource.Token) + .DebuggerStatementHandling(debuggerStatementHandlingMode) + .DebugMode(jintSettings.EnableDebugging) + .DisableStringCompilation(jintSettings.DisableEval) + .LimitMemory(jintSettings.MemoryLimit) + .LimitRecursion(jintSettings.MaxRecursionDepth) + .LocalTimeZone(jintSettings.LocalTimeZone ?? TimeZoneInfo.Local) + .MaxArraySize(jintSettings.MaxArraySize) + .MaxJsonParseDepth(jintSettings.MaxJsonParseDepth) + .MaxStatements(jintSettings.MaxStatements) + .Strict(jintSettings.StrictMode) + .TimeoutInterval(jintSettings.TimeoutInterval) + ; + + if (jintSettings.RegexTimeoutInterval.HasValue) + { + options.RegexTimeoutInterval(jintSettings.RegexTimeoutInterval.Value); + } + + options.AddObjectConverter(new UndefinedConverter()); + options.SetTypeResolver(jintSettings.AllowReflection ? + CustomTypeResolvers.AllowingReflection : CustomTypeResolvers.DisallowingReflection); + }); + _cancellationConstraint = _jsEngine.Constraints.Find(); + if (_debuggerBreakCallback != null) + { + _jsEngine.Debugger.Break += _debuggerBreakCallback; + } + if (_debuggerStepCallback != null) + { + _jsEngine.Debugger.Step += _debuggerStepCallback; + } + _strictMode = settings.StrictMode; } catch (Exception e) { - throw new JsEngineLoadException( - string.Format(CoreStrings.Runtime_JsEngineNotLoaded, - ENGINE_NAME, e.Message), ENGINE_NAME, ENGINE_VERSION, e); + throw JsErrorHelpers.WrapEngineLoadException(e, EngineName, EngineVersion, true); } } - #region JsEngineBase implementation + + #region Mapping /// - /// Executes a mapping from the host type to a Jint type + /// Makes a mapping of value from the host type to a script type /// /// The source value /// The mapped value - private OriginalJsValue MapToJintType(object value) + private OriginalValue MapToScriptType(object value) { - if (value is Undefined) - { - return OriginalJsValue.Undefined; - } - - return OriginalJsValue.FromObject(_jsEngine, value); + return OriginalValue.FromObject(_jsEngine, value); } /// - /// Executes a mapping from the Jint type to a host type + /// Makes a mapping of value from the script type to a host type /// /// The source value /// The mapped value - private object MapToHostType(OriginalJsValue value) + private object MapToHostType(OriginalValue value) { - if (value.IsUndefined()) + switch (value.Type) { - return Undefined.Value; + case OriginalTypes.Undefined: + return Undefined.Value; + + case OriginalTypes.Object: + if (!(value is IOriginalPrimitive)) + { + return value; + } + else + { + break; + } } return value.ToObject(); } - private JsRuntimeException ConvertParserExceptionToJsRuntimeException( - OriginalParserException jsParserException) + private WrapperScriptException WrapException(OriginalException originalException) { - string message = jsParserException.Description; + WrapperScriptException wrapperScriptException; + string message = originalException.Message; if (string.IsNullOrWhiteSpace(message)) { - message = jsParserException.Message; + message = "An unknown error occurred"; } - - var jsRuntimeException = new JsRuntimeException(message, ENGINE_NAME, ENGINE_VERSION, - jsParserException) + string description = message; + string type = string.Empty; + string documentName = string.Empty; + int lineNumber = 0; + int columnNumber = 0; + string callStack = string.Empty; + + if (originalException is OriginalJavaScriptException) { - Category = "ParserError", - LineNumber = jsParserException.LineNumber, - ColumnNumber = jsParserException.Column, - Source = jsParserException.Source, - HelpLink = jsParserException.HelpLink - }; + var originalJavaScriptException = (OriginalJavaScriptException)originalException; + documentName = originalJavaScriptException.Location.SourceFile; + OriginalErrorPosition errorPosition = originalJavaScriptException.Location.Start; + lineNumber = errorPosition.Line; + columnNumber = errorPosition.Column + 1; - return jsRuntimeException; - } + ErrorLocationItem[] callStackItems = JintJsErrorHelpers.ParseErrorLocation( + originalJavaScriptException.JavaScriptStackTrace); + if (callStackItems.Length > 0) + { + JintJsErrorHelpers.FixErrorLocationItems(callStackItems); + callStack = JsErrorHelpers.StringifyErrorLocationItems(callStackItems, true); + } - private JsRuntimeException ConvertJavaScriptExceptionToJsRuntimeException( - OriginalJsException jsException) - { - string category = string.Empty; - OriginalJsValue errorValue = jsException.Error; + OriginalValue errorValue = originalJavaScriptException.Error; + if (errorValue.IsObject()) + { + OriginalObjectInstance errorObject = errorValue.AsObject(); - if (errorValue.IsObject()) - { - OriginalObjectInstance errorObject = errorValue.AsObject(); - OriginalJsValue categoryPropertyValue = errorObject.Get("name"); + OriginalValue namePropertyValue = errorObject.Get("name"); + if (namePropertyValue.IsString()) + { + type = namePropertyValue.AsString(); + } + } - if (categoryPropertyValue.IsString()) + if (string.IsNullOrEmpty(type)) { - category = categoryPropertyValue.AsString(); + type = JsErrorType.Common; } + + if (type == JsErrorType.Syntax) + { + description = JintJsErrorHelpers.GetDescriptionFromSyntaxErrorMessage(message); + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, lineNumber, + columnNumber); + wrapperScriptException = new WrapperCompilationException(message, EngineName, EngineVersion, + originalJavaScriptException); + } + else + { + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, callStack); + wrapperScriptException = new WrapperRuntimeException(message, EngineName, EngineVersion, + originalJavaScriptException) + { + CallStack = callStack + }; + } + } + else if (originalException is OriginalScriptPreparationException) + { + ErrorLocationItem errorLocation; + JintJsErrorHelpers.ParseScriptPreparationErrorMessage(message, out description, out errorLocation); + + type = JsErrorType.Syntax; + documentName = errorLocation.DocumentName; + lineNumber = errorLocation.LineNumber; + columnNumber = errorLocation.ColumnNumber; + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, lineNumber, + columnNumber); + wrapperScriptException = new WrapperCompilationException(message, EngineName, EngineVersion, + originalException); } + else if (originalException is OriginalMemoryLimitExceededException) + { + type = JsErrorType.Common; + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, string.Empty); - var jsRuntimeException = new JsRuntimeException(jsException.Message, ENGINE_NAME, ENGINE_VERSION, - jsException) + wrapperScriptException = new WrapperRuntimeException(message, EngineName, EngineVersion, + originalException); + } + else if (originalException is OriginalRecursionDepthOverflowException) { - Category = category, - LineNumber = jsException.LineNumber, - ColumnNumber = jsException.Column, - Source = jsException.Source, - HelpLink = jsException.HelpLink - }; + var originalRecursionException = (OriginalRecursionDepthOverflowException)originalException; + callStack = JintJsErrorHelpers.ConvertCallChainToStack(originalRecursionException.CallChain); + type = JsErrorType.Range; + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, callStack); - return jsRuntimeException; - } + wrapperScriptException = new WrapperRuntimeException(message, EngineName, EngineVersion, + originalRecursionException) + { + CallStack = callStack + }; + } + else if (originalException is OriginalStatementsCountOverflowException) + { + type = JsErrorType.Range; + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, string.Empty); - private JsRuntimeException ConvertRecursionDepthOverflowExceptionToJsRuntimeException( - OriginalRecursionDepthOverflowException jsRecursionException) - { - string message = string.Format(Strings.Runtime_RecursionDepthOverflow, - jsRecursionException.CallChain); + wrapperScriptException = new WrapperRuntimeException(message, EngineName, EngineVersion, + originalException); + } + else if (originalException is OriginalExecutionCanceledException) + { + _cancellationTokenSource.Dispose(); + _cancellationTokenSource = new CancellationTokenSource(); + + _cancellationConstraint.Reset(_cancellationTokenSource.Token); + + type = JsErrorType.Common; + message = CoreStrings.Runtime_ScriptInterrupted; + description = message; - var jsRuntimeException = new JsRuntimeException(message, - ENGINE_NAME, ENGINE_VERSION, jsRecursionException) + wrapperScriptException = new WrapperInterruptedException(message, + EngineName, EngineVersion, originalException); + } + else { - Category = "RecursionDepthOverflowError", - Source = jsRecursionException.Source, - HelpLink = jsRecursionException.HelpLink - }; + type = JsErrorType.Common; + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, string.Empty); + + wrapperScriptException = new WrapperRuntimeException(message, EngineName, EngineVersion, + originalException); + } + + wrapperScriptException.Description = description; + wrapperScriptException.Type = type; + wrapperScriptException.DocumentName = documentName; + wrapperScriptException.LineNumber = lineNumber; + wrapperScriptException.ColumnNumber = columnNumber; - return jsRuntimeException; + return wrapperScriptException; } - private JsRuntimeException ConvertStatementsCountOverflowExceptionToJsRuntimeException( - OriginalStatementsCountOverflowException jsStatementsException) + private static WrapperTimeoutException WrapTimeoutException(TimeoutException originalTimeoutException) { - var jsRuntimeException = new JsRuntimeException(Strings.Runtime_StatementsCountOverflow, - ENGINE_NAME, ENGINE_VERSION) + string message = CoreStrings.Runtime_ScriptTimeoutExceeded; + string description = message; + + var wrapperTimeoutException = new WrapperTimeoutException(message, EngineName, EngineVersion, + originalTimeoutException) { - Category = "StatementsCountOverflowError", - Source = jsStatementsException.Source, - HelpLink = jsStatementsException.HelpLink + Description = description }; - return jsRuntimeException; + return wrapperTimeoutException; + } + + #endregion + + #region JsEngineBase overrides + + protected override IPrecompiledScript InnerPrecompile(string code) + { + return InnerPrecompile(code, null); } - private JsRuntimeException ConvertTimeoutExceptionToJsRuntimeException( - TimeoutException jsTimeoutException) + protected override IPrecompiledScript InnerPrecompile(string code, string documentName) { - var jsRuntimeException = new JsRuntimeException(Strings.Runtime_ExecutionTimeout, - ENGINE_NAME, ENGINE_VERSION) + OriginalParsedScript parsedScript; + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + + try { - Category = "TimeoutError", - Source = jsTimeoutException.Source, - HelpLink = jsTimeoutException.HelpLink - }; + parsedScript = OriginalEngine.PrepareScript(code, uniqueDocumentName, _strictMode); + } + catch (OriginalException e) + { + throw WrapException(e); + } - return jsRuntimeException; + return new JintPrecompiledScript(parsedScript); } protected override object InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override object InnerEvaluate(string expression, string documentName) { object result; + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); lock (_executionSynchronizer) { - OriginalJsValue resultValue; + OriginalValue resultValue; try { - resultValue = _jsEngine.Execute(expression).GetCompletionValue(); + resultValue = _jsEngine.Evaluate(expression, uniqueDocumentName); } - catch (OriginalParserException e) + catch (OriginalException e) { - throw ConvertParserExceptionToJsRuntimeException(e); - } - catch (OriginalJsException e) - { - throw ConvertJavaScriptExceptionToJsRuntimeException(e); - } - catch (OriginalRecursionDepthOverflowException e) - { - throw ConvertRecursionDepthOverflowExceptionToJsRuntimeException(e); - } - catch (OriginalStatementsCountOverflowException e) - { - throw ConvertStatementsCountOverflowExceptionToJsRuntimeException(e); + throw WrapException(e); } catch (TimeoutException e) { - throw ConvertTimeoutExceptionToJsRuntimeException(e); + throw WrapTimeoutException(e); } result = MapToHostType(resultValue); @@ -265,38 +418,67 @@ protected override object InnerEvaluate(string expression) protected override T InnerEvaluate(string expression) { - object result = InnerEvaluate(expression); + return InnerEvaluate(expression, null); + } + + protected override T InnerEvaluate(string expression, string documentName) + { + object result = InnerEvaluate(expression, documentName); return TypeConverter.ConvertToType(result); } protected override void InnerExecute(string code) { + InnerExecute(code, null); + } + + protected override void InnerExecute(string code, string documentName) + { + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + lock (_executionSynchronizer) { try { - _jsEngine.Execute(code); + _jsEngine.Execute(code, uniqueDocumentName); } - catch (OriginalParserException e) + catch (OriginalException e) { - throw ConvertParserExceptionToJsRuntimeException(e); + throw WrapException(e); } - catch (OriginalJsException e) + catch (TimeoutException e) { - throw ConvertJavaScriptExceptionToJsRuntimeException(e); + throw WrapTimeoutException(e); } - catch (OriginalRecursionDepthOverflowException e) + } + } + + protected override void InnerExecute(IPrecompiledScript precompiledScript) + { + var jintPrecompiledScript = precompiledScript as JintPrecompiledScript; + if (jintPrecompiledScript == null) + { + throw new WrapperUsageException( + string.Format(CoreStrings.Usage_CannotConvertPrecompiledScriptToInternalType, + typeof(JintPrecompiledScript).FullName), + Name, Version + ); + } + + lock (_executionSynchronizer) + { + try { - throw ConvertRecursionDepthOverflowExceptionToJsRuntimeException(e); + _jsEngine.Execute(jintPrecompiledScript.ParsedScript); } - catch (OriginalStatementsCountOverflowException e) + catch (OriginalException e) { - throw ConvertStatementsCountOverflowExceptionToJsRuntimeException(e); + throw WrapException(e); } catch (TimeoutException e) { - throw ConvertTimeoutExceptionToJsRuntimeException(e); + throw WrapTimeoutException(e); } } } @@ -307,56 +489,35 @@ protected override object InnerCallFunction(string functionName, params object[] lock (_executionSynchronizer) { - OriginalJsValue functionValue; + OriginalValue functionValue; try { functionValue = _jsEngine.GetValue(functionName); } - catch (OriginalJsException e) - { - throw ConvertJavaScriptExceptionToJsRuntimeException(e); - } - - var callable = functionValue.TryCast(); - if (callable == null) + catch (OriginalException e) { - throw new JsRuntimeException( - string.Format(CoreStrings.Runtime_FunctionNotExist, functionName)); + throw WrapException(e); } - int argumentCount = args.Length; - var processedArgs = new OriginalJsValue[argumentCount]; - - if (argumentCount > 0) - { - for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) - { - processedArgs[argumentIndex] = MapToJintType(args[argumentIndex]); - } - } - - OriginalJsValue resultValue; + OriginalValue resultValue; try { - resultValue = callable.Call(functionValue, processedArgs); - } - catch (OriginalJsException e) - { - throw ConvertJavaScriptExceptionToJsRuntimeException(e); + resultValue = _jsEngine.Invoke(functionValue, args); } - catch (OriginalRecursionDepthOverflowException e) + catch (OriginalException e) { - throw ConvertRecursionDepthOverflowExceptionToJsRuntimeException(e); + throw WrapException(e); } - catch (OriginalStatementsCountOverflowException e) + catch (TimeoutException e) { - throw ConvertStatementsCountOverflowExceptionToJsRuntimeException(e); + throw WrapTimeoutException(e); } - catch (TimeoutException e) + catch (ArgumentException e) when (e.Message == "Can only invoke functions") { - throw ConvertTimeoutExceptionToJsRuntimeException(e); + throw new WrapperRuntimeException( + string.Format(CoreStrings.Runtime_FunctionNotExist, functionName)); } result = MapToHostType(resultValue); @@ -380,10 +541,10 @@ protected override bool InnerHasVariable(string variableName) { try { - OriginalJsValue variableValue = _jsEngine.GetValue(variableName); + OriginalValue variableValue = _jsEngine.GetValue(variableName); result = !variableValue.IsUndefined(); } - catch (OriginalJsException) + catch (OriginalJavaScriptException) { result = false; } @@ -398,15 +559,15 @@ protected override object InnerGetVariableValue(string variableName) lock (_executionSynchronizer) { - OriginalJsValue variableValue; + OriginalValue variableValue; try { variableValue = _jsEngine.GetValue(variableName); } - catch (OriginalJsException e) + catch (OriginalException e) { - throw ConvertJavaScriptExceptionToJsRuntimeException(e); + throw WrapException(e); } result = MapToHostType(variableValue); @@ -426,15 +587,15 @@ protected override void InnerSetVariableValue(string variableName, object value) { lock (_executionSynchronizer) { - OriginalJsValue processedValue = MapToJintType(value); + OriginalValue processedValue = MapToScriptType(value); try { _jsEngine.SetValue(variableName, processedValue); } - catch (OriginalJsException e) + catch (OriginalException e) { - throw ConvertJavaScriptExceptionToJsRuntimeException(e); + throw WrapException(e); } } } @@ -448,15 +609,15 @@ protected override void InnerEmbedHostObject(string itemName, object value) { lock (_executionSynchronizer) { - OriginalJsValue processedValue = MapToJintType(value); + OriginalValue processedValue = MapToScriptType(value); try { _jsEngine.SetValue(itemName, processedValue); } - catch (OriginalJsException e) + catch (OriginalException e) { - throw ConvertJavaScriptExceptionToJsRuntimeException(e); + throw WrapException(e); } } } @@ -471,13 +632,50 @@ protected override void InnerEmbedHostType(string itemName, Type type) { _jsEngine.SetValue(itemName, typeReference); } - catch (OriginalJsException e) + catch (OriginalException e) { - throw ConvertJavaScriptExceptionToJsRuntimeException(e); + throw WrapException(e); } } } + protected override void InnerInterrupt() + { + _cancellationTokenSource.Cancel(); + } + + protected override void InnerCollectGarbage() + { + throw new NotSupportedException(); + } + + #region IJsEngine implementation + + public override string Name + { + get { return EngineName; } + } + + public override string Version + { + get { return EngineVersion; } + } + + public override bool SupportsScriptPrecompilation + { + get { return true; } + } + + public override bool SupportsScriptInterruption + { + get { return true; } + } + + public override bool SupportsGarbageCollection + { + get { return false; } + } + #endregion #region IDisposable implementation @@ -488,11 +686,37 @@ public override void Dispose() { lock (_executionSynchronizer) { - _jsEngine = null; + if (_jsEngine != null) + { + if (_debuggerStepCallback != null) + { + _jsEngine.Debugger.Step -= _debuggerStepCallback; + } + + if (_debuggerBreakCallback != null) + { + _jsEngine.Debugger.Break -= _debuggerBreakCallback; + } + + _jsEngine.Dispose(); + _jsEngine = null; + } + + _debuggerStepCallback = null; + _debuggerBreakCallback = null; + _cancellationConstraint = null; + + if (_cancellationTokenSource != null) + { + _cancellationTokenSource.Dispose(); + _cancellationTokenSource = null; + } } } } #endregion + + #endregion } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/JintJsEngineFactory.cs b/src/JavaScriptEngineSwitcher.Jint/JintJsEngineFactory.cs new file mode 100644 index 00000000..5d272d8e --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jint/JintJsEngineFactory.cs @@ -0,0 +1,53 @@ +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Jint +{ + /// + /// Jint JS engine factory + /// + public sealed class JintJsEngineFactory : IJsEngineFactory + { + /// + /// Settings of the Jint JS engine + /// + private readonly JintSettings _settings; + + + /// + /// Constructs an instance of the Jint JS engine factory + /// + public JintJsEngineFactory() + : this(new JintSettings()) + { } + + /// + /// Constructs an instance of the Jint JS engine factory + /// + /// Settings of the Jint JS engine + public JintJsEngineFactory(JintSettings settings) + { + _settings = settings; + } + + + #region IJsEngineFactory implementation + + /// + public string EngineName + { + get { return JintJsEngine.EngineName; } + } + + + /// + /// Creates a instance of the Jint JS engine + /// + /// Instance of the Jint JS engine + public IJsEngine CreateEngine() + { + return new JintJsEngine(_settings); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/JintPrecompiledScript.cs b/src/JavaScriptEngineSwitcher.Jint/JintPrecompiledScript.cs new file mode 100644 index 00000000..0d36e5b9 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jint/JintPrecompiledScript.cs @@ -0,0 +1,42 @@ +using OriginalParsedScript = Jint.Prepared; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Jint +{ + /// + /// Represents a pre-compiled script that can be executed by different instances of the Jint JS engine + /// + internal sealed class JintPrecompiledScript : IPrecompiledScript + { + /// + /// Gets a parsed script + /// + public OriginalParsedScript ParsedScript + { + get; + private set; + } + + + /// + /// Constructs an instance of pre-compiled script + /// + /// The parsed script + public JintPrecompiledScript(OriginalParsedScript parsedScript) + { + ParsedScript = parsedScript; + } + + + #region IPrecompiledScript implementation + + /// + public string EngineName + { + get { return JintJsEngine.EngineName; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/JintSettings.cs b/src/JavaScriptEngineSwitcher.Jint/JintSettings.cs new file mode 100644 index 00000000..966360a1 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jint/JintSettings.cs @@ -0,0 +1,202 @@ +using System; + +using OriginalDebuggerEventHandler = Jint.Runtime.Debugger.DebugHandler.DebugEventHandler; + +namespace JavaScriptEngineSwitcher.Jint +{ + /// + /// Settings of the Jint JS engine + /// + public sealed class JintSettings + { + /// + /// Gets or sets a flag for whether to allow the usage of reflection API in the script code + /// + /// + /// This affects , Exception.GetType, + /// Exception.TargetSite and Delegate.Method. + /// + public bool AllowReflection + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to allow the debugger statement + /// to be called in a script + /// + [Obsolete("Use a `DebuggerStatementHandlingMode` property")] + public bool AllowDebuggerStatement + { + get { return DebuggerStatementHandlingMode == JsDebuggerStatementHandlingMode.Clr; } + set { DebuggerStatementHandlingMode = value ? JsDebuggerStatementHandlingMode.Clr : JsDebuggerStatementHandlingMode.Ignore; } + } + + /// + /// Gets or sets a debugger break callback + /// + public OriginalDebuggerEventHandler DebuggerBreakCallback + { + get; + set; + } + + /// + /// Gets or sets a handling mode for script debugger statements + /// + public JsDebuggerStatementHandlingMode DebuggerStatementHandlingMode + { + get; + set; + } + + /// + /// Gets or sets a debugger step callback + /// + public OriginalDebuggerEventHandler DebuggerStepCallback + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to disable calls of eval function with custom code + /// and Function constructors taking function code as string + /// + public bool DisableEval + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to enable debug mode + /// + public bool EnableDebugging + { + get; + set; + } + + /// + /// Gets or sets a local time zone for the Date objects in the script + /// + public TimeZoneInfo LocalTimeZone + { + get; + set; + } + + /// + /// Gets or sets a maximum size for JavaScript array + /// + public uint MaxArraySize + { + get; + set; + } + + /// + /// Gets or sets a maximum depth allowed when parsing JSON data using the JSON.parse static method + /// + public int MaxJsonParseDepth + { + get; + set; + } + + /// + /// Gets or sets a maximum allowed depth of recursion: + /// -1 - recursion without limits; + /// N - one scope function can be called no more than N times. + /// + public int MaxRecursionDepth + { + get; + set; + } + + /// + /// Gets or sets a maximum number of statements + /// + public int MaxStatements + { + get; + set; + } + + /// + /// Gets or sets a current memory limit for a engine in bytes + /// + public long MemoryLimit + { + get; + set; + } + + /// + /// Gets or sets a timeout interval for regular expressions + /// + /// + /// If the value of this property is null, then the value of regular expression + /// timeout interval are taken from the property. + /// + public TimeSpan? RegexTimeoutInterval + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to allow run the script in strict mode + /// + public bool StrictMode + { + get; + set; + } + + /// + /// Gets or sets a number of milliseconds to wait before the script execution times out + /// + [Obsolete("Use a `TimeoutInterval` property")] + public int Timeout + { + get { return TimeoutInterval.Milliseconds; } + set { TimeoutInterval = TimeSpan.FromMilliseconds(value); } + } + + /// + /// Gets or sets a interval to wait before the script execution times out + /// + public TimeSpan TimeoutInterval + { + get; + set; + } + + + /// + /// Constructs an instance of the Jint settings + /// + public JintSettings() + { + AllowReflection = false; + DebuggerBreakCallback = null; + DebuggerStatementHandlingMode = JsDebuggerStatementHandlingMode.Ignore; + DebuggerStepCallback = null; + DisableEval = false; + EnableDebugging = false; + LocalTimeZone = TimeZoneInfo.Local; + MaxArraySize = uint.MaxValue; + MaxJsonParseDepth = 64; + MaxRecursionDepth = -1; + MaxStatements = 0; + MemoryLimit = 0; + RegexTimeoutInterval = null; + StrictMode = false; + TimeoutInterval = TimeSpan.Zero; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/JsDebuggerStatementHandlingMode.cs b/src/JavaScriptEngineSwitcher.Jint/JsDebuggerStatementHandlingMode.cs new file mode 100644 index 00000000..463b6fc2 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jint/JsDebuggerStatementHandlingMode.cs @@ -0,0 +1,26 @@ +namespace JavaScriptEngineSwitcher.Jint +{ + /// + /// Handling mode for script debugger statements + /// + public enum JsDebuggerStatementHandlingMode + { + /// + /// No action will be taken when encountering a debugger statement + /// + Ignore, + + /// + /// debugger statements will trigger debugging through + /// + Clr, + + /// + /// debugger statements will trigger a break in Jint's DebugHandler + /// + /// + /// See the configuration property. + /// + Script + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/JsEngineFactoryCollectionExtensions.cs b/src/JavaScriptEngineSwitcher.Jint/JsEngineFactoryCollectionExtensions.cs new file mode 100644 index 00000000..fd0f6e6a --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jint/JsEngineFactoryCollectionExtensions.cs @@ -0,0 +1,78 @@ +using System; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Jint +{ + /// + /// JS engine factory collection extensions + /// + public static class JsEngineFactoryCollectionExtensions + { + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Instance of + public static JsEngineFactoryCollection AddJint(this JsEngineFactoryCollection source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return source.AddJint(new JintSettings()); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The delegate to configure the provided + /// Instance of + public static JsEngineFactoryCollection AddJint(this JsEngineFactoryCollection source, + Action configure) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var settings = new JintSettings(); + configure(settings); + + return source.AddJint(settings); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Settings of the Jint JS engine + /// Instance of + public static JsEngineFactoryCollection AddJint(this JsEngineFactoryCollection source, JintSettings settings) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + source.Add(new JintJsEngineFactory(settings)); + + return source; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/JsEngineSwitcherExtensions.cs b/src/JavaScriptEngineSwitcher.Jint/JsEngineSwitcherExtensions.cs deleted file mode 100644 index bcc9d52d..00000000 --- a/src/JavaScriptEngineSwitcher.Jint/JsEngineSwitcherExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace JavaScriptEngineSwitcher.Jint -{ - using System; - using System.Configuration; - - using Core; - using Configuration; - - /// - /// JavaScript engine switcher extensions - /// - public static class JsEngineSwitcherExtensions - { - /// - /// Configuration settings of Jint JavaScript engine - /// - private static readonly Lazy _jintConfig = - new Lazy(() => (JintConfiguration)ConfigurationManager.GetSection("jsEngineSwitcher/jint")); - - /// - /// Gets a Jint JavaScript engine configuration settings - /// - /// JavaScript engine switcher> - /// Configuration settings of Jint JavaScript engine - public static JintConfiguration GetJintConfiguration(this JsEngineSwitcher switcher) - { - return _jintConfig.Value; - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.Jint/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..9c58f953 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jint/PACKAGE-DESCRIPTION.md @@ -0,0 +1 @@ +JavaScriptEngineSwitcher.Jint contains a `JintJsEngine` adapter (wrapper for the [Jint](http://github.com/sebastienros/jint) version 4.4.2). \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/Properties/AssemblyInfo.cs b/src/JavaScriptEngineSwitcher.Jint/Properties/AssemblyInfo.cs deleted file mode 100644 index 1c493e96..00000000 --- a/src/JavaScriptEngineSwitcher.Jint/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("JavaScriptEngineSwitcher.Jint")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JavaScript Engine Switcher for .Net: Jint")] -[assembly: AssemblyCopyright("Copyright © 2013-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("4b51319e-014f-4731-8a80-15b5c58f3a72")] - -[assembly: AssemblyVersion("1.5.4.0")] -[assembly: AssemblyFileVersion("1.5.4.0")] \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/Resources/Strings.Designer.cs b/src/JavaScriptEngineSwitcher.Jint/Resources/Strings.Designer.cs deleted file mode 100644 index e77b5cd4..00000000 --- a/src/JavaScriptEngineSwitcher.Jint/Resources/Strings.Designer.cs +++ /dev/null @@ -1,90 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.34209 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace JavaScriptEngineSwitcher.Jint.Resources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Strings { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Strings() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JavaScriptEngineSwitcher.Jint.Resources.Strings", typeof(Strings).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Maximum execution time exceeded.. - /// - internal static string Runtime_ExecutionTimeout { - get { - return ResourceManager.GetString("Runtime_ExecutionTimeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Recursion is forbidden by script host: {0}. - /// - internal static string Runtime_RecursionDepthOverflow { - get { - return ResourceManager.GetString("Runtime_RecursionDepthOverflow", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maximum number of statements executed have been reached.. - /// - internal static string Runtime_StatementsCountOverflow { - get { - return ResourceManager.GetString("Runtime_StatementsCountOverflow", resourceCulture); - } - } - } -} diff --git a/src/JavaScriptEngineSwitcher.Jint/UndefinedConverter.cs b/src/JavaScriptEngineSwitcher.Jint/UndefinedConverter.cs new file mode 100644 index 00000000..4ee5f399 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jint/UndefinedConverter.cs @@ -0,0 +1,26 @@ +using IOriginalObjectConverter = Jint.Runtime.Interop.IObjectConverter; +using OriginalEngine = Jint.Engine; +using OriginalValue = Jint.Native.JsValue; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Jint +{ + /// + /// Converts a instance to a instance + /// + internal sealed class UndefinedConverter : IOriginalObjectConverter + { + public bool TryConvert(OriginalEngine engine, object value, out OriginalValue result) + { + if (value is Undefined) + { + result = OriginalValue.Undefined; + return true; + } + + result = OriginalValue.Null; + return false; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/readme.txt b/src/JavaScriptEngineSwitcher.Jint/readme.txt new file mode 100644 index 00000000..02775102 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jint/readme.txt @@ -0,0 +1,26 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: Jint v3.30.2 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2025 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + JavaScriptEngineSwitcher.Jint contains a `JintJsEngine` adapter (wrapper for the + Jint (http://github.com/sebastienros/jint) version 4.4.2). + + ============= + RELEASE NOTES + ============= + Jint was updated to version 4.4.2. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/Configuration/JurassicConfiguration.cs b/src/JavaScriptEngineSwitcher.Jurassic/Configuration/JurassicConfiguration.cs deleted file mode 100644 index 747f264f..00000000 --- a/src/JavaScriptEngineSwitcher.Jurassic/Configuration/JurassicConfiguration.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace JavaScriptEngineSwitcher.Jurassic.Configuration -{ - using System.Configuration; - - /// - /// Configuration settings of Jurassic JavaScript engine - /// - public sealed class JurassicConfiguration : ConfigurationSection - { - /// - /// Gets or sets a flag for whether to enable script debugging features - /// (allows a generation of debug information) - /// - [ConfigurationProperty("enableDebugging", DefaultValue = false)] - public bool EnableDebugging - { - get { return (bool)this["enableDebugging"]; } - set { this["enableDebugging"] = value; } - } - - /// - /// Gets or sets a flag for whether to disassemble any generated IL - /// and store it in the associated function - /// - [ConfigurationProperty("enableIlAnalysis", DefaultValue = false)] - public bool EnableIlAnalysis - { - get { return (bool)this["enableIlAnalysis"]; } - set { this["enableIlAnalysis"] = value; } - } - - /// - /// Gets or sets a flag for whether to allow run the script in strict mode - /// - [ConfigurationProperty("strictMode", DefaultValue = false)] - public bool StrictMode - { - get { return (bool)this["strictMode"]; } - set { this["strictMode"] = value; } - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/FileScriptSource.cs b/src/JavaScriptEngineSwitcher.Jurassic/FileScriptSource.cs new file mode 100644 index 00000000..3d8941e9 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jurassic/FileScriptSource.cs @@ -0,0 +1,101 @@ +using System; +using System.IO; +using System.Text; + +using OriginalScriptSource = Jurassic.ScriptSource; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; + +namespace JavaScriptEngineSwitcher.Jurassic +{ + /// + /// Represents a JS file + /// + internal sealed class FileScriptSource : OriginalScriptSource + { + /// + /// The document name + /// + private readonly string _documentName; + + /// + /// The path to the JS file + /// + private readonly string _path; + + /// + /// The text encoding + /// + private readonly Encoding _encoding; + + + /// + /// Constructs an instance of + /// + /// The document name + /// The path to the JS file + /// The text encoding + public FileScriptSource(string documentName, string path, Encoding encoding = null) + { + if (documentName == null) + { + throw new ArgumentNullException( + nameof(documentName), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(documentName)) + ); + } + + if (path == null) + { + throw new ArgumentNullException( + nameof(path), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(path)) + ); + } + + if (string.IsNullOrWhiteSpace(documentName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(documentName)), + nameof(documentName) + ); + } + + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(path)), + nameof(path) + ); + } + + _documentName = documentName; + _path = path; + _encoding = encoding ?? Encoding.UTF8; + } + + + #region Jurassic.ScriptSource overrides + + /// + /// Gets a document name + /// + public override string Path + { + get { return _documentName; } + } + + + /// + /// Gets a reader that can be used to read the source code from JS file + /// + /// A reader that can be used to read the source code from JS file, + /// positioned at the start of the source code + public override TextReader GetReader() + { + return new StreamReader(_path, _encoding, true); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/JavaScriptEngineSwitcher.Jurassic.csproj b/src/JavaScriptEngineSwitcher.Jurassic/JavaScriptEngineSwitcher.Jurassic.csproj index a4fef0a6..d2bf3a2b 100644 --- a/src/JavaScriptEngineSwitcher.Jurassic/JavaScriptEngineSwitcher.Jurassic.csproj +++ b/src/JavaScriptEngineSwitcher.Jurassic/JavaScriptEngineSwitcher.Jurassic.csproj @@ -1,73 +1,37 @@ - - - + + - Debug - AnyCPU - {2E667689-F072-401F-A9A5-09F1A2ED025C} + JS Engine Switcher: Jurassic + 3.29.0 + net40-client;net45;netstandard2.0 Library - Properties - JavaScriptEngineSwitcher.Jurassic - JavaScriptEngineSwitcher.Jurassic - v4.0 - 512 - Client - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true + true + $(NoWarn);CS1591 + false + true + + + + + + - ..\..\JavaScriptEngineSwitcher.snk + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Jurassic_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_Jurassic_Logo128x128.png + JavaScriptEngineSwitcher.Jurassic contains a `JurassicJsEngine` adapter (wrapper for the Jurassic). + $(PackageCommonTags);Jurassic + Jurassic was updated to version of February 4, 2025. + - - {5c903eef-bad1-43b8-bfe2-e4ee4d204410} - JavaScriptEngineSwitcher.Core - - - - - False - ..\..\Binaries\Jurassic\Jurassic.dll - - - - - - - - - - + + + + - - JavaScriptEngineSwitcher.snk - + - - + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/JsEngineFactoryCollectionExtensions.cs b/src/JavaScriptEngineSwitcher.Jurassic/JsEngineFactoryCollectionExtensions.cs new file mode 100644 index 00000000..265afab0 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jurassic/JsEngineFactoryCollectionExtensions.cs @@ -0,0 +1,79 @@ +using System; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Jurassic +{ + /// + /// JS engine factory collection extensions + /// + public static class JsEngineFactoryCollectionExtensions + { + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Instance of + public static JsEngineFactoryCollection AddJurassic(this JsEngineFactoryCollection source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return source.AddJurassic(new JurassicSettings()); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The delegate to configure the provided + /// Instance of + public static JsEngineFactoryCollection AddJurassic(this JsEngineFactoryCollection source, + Action configure) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var settings = new JurassicSettings(); + configure(settings); + + return source.AddJurassic(settings); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Settings of the Jurassic JS engine + /// Instance of + public static JsEngineFactoryCollection AddJurassic(this JsEngineFactoryCollection source, + JurassicSettings settings) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + source.Add(new JurassicJsEngineFactory(settings)); + + return source; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/JsEngineSwitcherExtensions.cs b/src/JavaScriptEngineSwitcher.Jurassic/JsEngineSwitcherExtensions.cs deleted file mode 100644 index 429f2f01..00000000 --- a/src/JavaScriptEngineSwitcher.Jurassic/JsEngineSwitcherExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace JavaScriptEngineSwitcher.Jurassic -{ - using System; - using System.Configuration; - - using Core; - using Configuration; - - /// - /// JavaScript engine switcher extensions - /// - public static class JsEngineSwitcherExtensions - { - /// - /// Configuration settings of Jurassic JavaScript engine - /// - private static readonly Lazy _jurassicConfig = - new Lazy(() => (JurassicConfiguration)ConfigurationManager.GetSection("jsEngineSwitcher/jurassic")); - - /// - /// Gets a Jurassic JavaScript engine configuration settings - /// - /// JavaScript engine switcher> - /// Configuration settings of Jurassic JavaScript engine - public static JurassicConfiguration GetJurassicConfiguration(this JsEngineSwitcher switcher) - { - return _jurassicConfig.Value; - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/JurassicJsEngine.cs b/src/JavaScriptEngineSwitcher.Jurassic/JurassicJsEngine.cs index 9ed5a5c3..57d9976b 100644 --- a/src/JavaScriptEngineSwitcher.Jurassic/JurassicJsEngine.cs +++ b/src/JavaScriptEngineSwitcher.Jurassic/JurassicJsEngine.cs @@ -1,41 +1,56 @@ -using OriginalCompatibilityMode = Jurassic.CompatibilityMode; +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; + +using OriginalCompatibilityMode = Jurassic.CompatibilityMode; +using OriginalCompiledScript = Jurassic.CompiledScript; using OriginalConcatenatedString = Jurassic.ConcatenatedString; -using OriginalJsEngine = Jurassic.ScriptEngine; -using OriginalJsException = Jurassic.JavaScriptException; +using OriginalEngine = Jurassic.ScriptEngine; +using OriginalErrorInstance = Jurassic.Library.ErrorInstance; +using OriginalJavaScriptException = Jurassic.JavaScriptException; using OriginalNull = Jurassic.Null; +using OriginalStringScriptSource = Jurassic.StringScriptSource; +using OriginalSyntaxException = Jurassic.Compiler.SyntaxErrorException; using OriginalTypeConverter = Jurassic.TypeConverter; using OriginalUndefined = Jurassic.Undefined; -namespace JavaScriptEngineSwitcher.Jurassic -{ - using System; - using System.IO; - using System.Text; - - using Core; - using CoreStrings = Core.Resources.Strings; +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Constants; +using JavaScriptEngineSwitcher.Core.Extensions; +using JavaScriptEngineSwitcher.Core.Helpers; +using JavaScriptEngineSwitcher.Core.Utilities; - using Configuration; +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; +using WrapperCompilationException = JavaScriptEngineSwitcher.Core.JsCompilationException; +using WrapperException = JavaScriptEngineSwitcher.Core.JsException; +using WrapperRuntimeException = JavaScriptEngineSwitcher.Core.JsRuntimeException; +using WrapperScriptException = JavaScriptEngineSwitcher.Core.JsScriptException; +using WrapperUsageException = JavaScriptEngineSwitcher.Core.JsUsageException; +namespace JavaScriptEngineSwitcher.Jurassic +{ /// - /// Adapter for Jurassic JavaScript engine + /// Adapter for the Jurassic JS engine /// public sealed class JurassicJsEngine : JsEngineBase { /// - /// Name of JavaScript engine + /// Name of JS engine /// - private const string ENGINE_NAME = "Jurassic JavaScript engine"; + public const string EngineName = "JurassicJsEngine"; /// - /// Version of original JavaScript engine + /// Version of original JS engine /// - private const string ENGINE_VERSION = "Jun 29, 2016"; + private const string EngineVersion = "Feb 4, 2025"; /// /// Jurassic JS engine /// - private OriginalJsEngine _jsEngine; + private OriginalEngine _jsEngine; /// /// Synchronizer of code execution @@ -43,62 +58,53 @@ public sealed class JurassicJsEngine : JsEngineBase private readonly object _executionSynchronizer = new object(); /// - /// Gets a name of JavaScript engine + /// Unique document name manager /// - public override string Name - { - get { return ENGINE_NAME; } - } - - /// - /// Gets a version of original JavaScript engine - /// - public override string Version - { - get { return ENGINE_VERSION; } - } + private readonly UniqueDocumentNameManager _documentNameManager = + new UniqueDocumentNameManager(DefaultDocumentName); /// - /// Constructs a instance of adapter for Jurassic + /// Constructs an instance of adapter for the Jurassic JS engine /// public JurassicJsEngine() - : this(JsEngineSwitcher.Current.GetJurassicConfiguration()) + : this(new JurassicSettings()) { } /// - /// Constructs a instance of adapter for Jurassic + /// Constructs an instance of adapter for the Jurassic JS engine /// - /// Configuration settings of Jurassic JavaScript engine - public JurassicJsEngine(JurassicConfiguration config) + /// Settings of the Jurassic JS engine + public JurassicJsEngine(JurassicSettings settings) { - JurassicConfiguration jurassicConfig = config ?? new JurassicConfiguration(); + JurassicSettings jurassicSettings = settings ?? new JurassicSettings(); try { - _jsEngine = new OriginalJsEngine + _jsEngine = new OriginalEngine { - EnableDebugging = jurassicConfig.EnableDebugging, CompatibilityMode = OriginalCompatibilityMode.Latest, + DisableClrCollectionsExposingByValue = !jurassicSettings.EnableHostCollectionsEmbeddingByValue, EnableExposedClrTypes = true, - EnableILAnalysis = jurassicConfig.EnableIlAnalysis, - ForceStrictMode = jurassicConfig.StrictMode + EnableILAnalysis = jurassicSettings.EnableIlAnalysis, + ForceStrictMode = jurassicSettings.StrictMode }; } catch (Exception e) { - throw new JsEngineLoadException( - string.Format(CoreStrings.Runtime_JsEngineNotLoaded, - ENGINE_NAME, e.Message), ENGINE_NAME, ENGINE_VERSION, e); + throw JsErrorHelpers.WrapEngineLoadException(e, EngineName, EngineVersion, true); } } + + #region Mapping + /// - /// Executes a mapping from the host type to a Jurassic type + /// Makes a mapping of value from the host type to a script type /// /// The source value /// The mapped value - private static object MapToJurassicType(object value) + private static object MapToScriptType(object value) { if (value == null) { @@ -114,141 +120,433 @@ private static object MapToJurassicType(object value) } /// - /// Executes a mapping from the Jurassic type to a host type + /// Makes a mapping of array items from the host type to a script type + /// + /// The source array + /// The mapped array + private static object[] MapToScriptType(object[] args) + { + return args.Select(MapToScriptType).ToArray(); + } + + /// + /// Makes a mapping of value from the script type to a host type /// /// The source value /// The mapped value private static object MapToHostType(object value) { + if (value is OriginalNull) + { + return null; + } + + if (value is OriginalUndefined) + { + return Undefined.Value; + } + if (value is OriginalConcatenatedString) { return value.ToString(); } + return value; + } + + /// + /// Makes a mapping of value from the script type to a host type + /// + /// The type to convert the value to + /// Original JS engine + /// The source value + /// The mapped value + private static T MapToHostType(OriginalEngine engine, object value) + { if (value is OriginalNull) { - return null; + return TypeConverter.ConvertToType(null); } - if (value is OriginalUndefined) + Type targetType = typeof(T); + + if (targetType == typeof(Undefined)) { - return Undefined.Value; + if (value is OriginalUndefined) + { + return (T)(object)Undefined.Value; + } + else + { + throw new InvalidOperationException( + string.Format(CoreStrings.Common_CannotConvertObjectToType, value.GetType(), targetType) + ); + } } - return value; + T result; + + try + { + result = OriginalTypeConverter.ConvertTo(engine, value); + } + catch (OriginalJavaScriptException e) + { + throw new InvalidOperationException(e.ErrorMessage, e); + } + catch (ArgumentException e) + { + if (targetType == typeof(string) && value != null) + { + return (T)(object)value.ToString(); + } + + throw new InvalidOperationException(e.Message, e); + } + + return result; } - private JsRuntimeException ConvertJavascriptExceptionToJsRuntimeException( - OriginalJsException jsException) + private static WrapperCompilationException WrapSyntaxException( + OriginalSyntaxException originalSyntaxException) { - var jsRuntimeException = new JsRuntimeException(jsException.Message, ENGINE_NAME, ENGINE_VERSION, - jsException) + string description = originalSyntaxException.Message; + string type = JsErrorType.Syntax; + string documentName = originalSyntaxException.SourcePath; + int lineNumber = originalSyntaxException.LineNumber; + string message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, + lineNumber, 0); + + var wrapperCompilationException = new WrapperCompilationException(message, EngineName, EngineVersion, + originalSyntaxException) { - Category = jsException.Name, - LineNumber = jsException.LineNumber, - ColumnNumber = 0, - SourceFragment = jsException.SourcePath, - Source = jsException.Source, - HelpLink = jsException.HelpLink + Description = description, + Type = type, + DocumentName = documentName, + LineNumber = lineNumber }; - return jsRuntimeException; + return wrapperCompilationException; } - #region JsEngineBase implementation - - protected override object InnerEvaluate(string expression) + private static WrapperException WrapJavaScriptException(OriginalEngine engine, + OriginalJavaScriptException originalJavaScriptException) { - object result; + WrapperException wrapperException; + string message = originalJavaScriptException.Message; + string messageWithCallStack = string.Empty; + string description = originalJavaScriptException.ErrorMessage; + string type = originalJavaScriptException.ErrorType.ToString(); + string documentName = originalJavaScriptException.SourcePath ?? string.Empty; + int lineNumber = originalJavaScriptException.LineNumber; + string callStack = string.Empty; - lock (_executionSynchronizer) + object errorObject = originalJavaScriptException.GetErrorObject(engine); + var errorValue = errorObject as OriginalErrorInstance; + if (errorValue != null) { - try + messageWithCallStack = errorValue.Stack; + } + + if (!string.IsNullOrEmpty(type)) + { + WrapperScriptException wrapperScriptException; + if (type == JsErrorType.Syntax) { - result = _jsEngine.Evaluate(expression); + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, + lineNumber, 0); + + wrapperScriptException = new WrapperCompilationException(message, EngineName, EngineVersion, + originalJavaScriptException); } - catch (OriginalJsException e) + else { - throw ConvertJavascriptExceptionToJsRuntimeException(e); + if (message.Length < messageWithCallStack.Length) + { + string rawCallStack = messageWithCallStack + .TrimStart(message) + .TrimStart(new char[] { '\n', '\r' }) + ; + ErrorLocationItem[] callStackItems = JsErrorHelpers.ParseErrorLocation(rawCallStack); + + if (callStackItems.Length > 0) + { + FixCallStackItems(callStackItems); + callStack = JsErrorHelpers.StringifyErrorLocationItems(callStackItems); + + if (string.IsNullOrWhiteSpace(documentName)) + { + documentName = callStackItems[0].DocumentName; + } + } + } + + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, callStack); + + wrapperScriptException = new WrapperRuntimeException(message, EngineName, EngineVersion, + originalJavaScriptException) + { + CallStack = callStack + }; + } + wrapperScriptException.Type = type; + wrapperScriptException.DocumentName = documentName; + wrapperScriptException.LineNumber = lineNumber; + + wrapperException = wrapperScriptException; + } + else + { + wrapperException = new WrapperException(message, EngineName, EngineVersion, + originalJavaScriptException); + } + + wrapperException.Description = description; + + return wrapperException; + } + + /// + /// Fixes a function name in call stack items + /// + /// An array of instances + private static void FixCallStackItems(ErrorLocationItem[] callStackItems) + { + foreach (ErrorLocationItem callStackItem in callStackItems) + { + string functionName = callStackItem.FunctionName; + if (functionName.Length > 0) + { + if (functionName == "anonymous") + { + callStackItem.FunctionName = "Anonymous function"; + } } - catch (NotImplementedException e) + else { - throw new JsRuntimeException(e.Message, e); + callStackItem.FunctionName = "Global code"; } } + } + + #endregion + + /// + /// Evaluates an expression without converting its result to a host type + /// + /// Original JS engine + /// JS expression + /// Unique document name + /// Result of the expression not converted to a host type + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static object InnerEvaluateWithoutResultConversion(OriginalEngine engine, string expression, + string uniqueDocumentName) + { + object result; + + try + { + var source = new OriginalStringScriptSource(expression, uniqueDocumentName); + result = engine.Evaluate(source); + } + catch (OriginalJavaScriptException e) + { + throw WrapJavaScriptException(engine, e); + } - result = MapToHostType(result); + return result; + } + + /// + /// Calls a function without converting its result to a host type + /// + /// Original JS engine + /// Function name + /// Function arguments converted to a script type + /// Result of the function execution not converted to a host type + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static object InnerCallFunctionWithoutResultConversion(OriginalEngine engine, string functionName, + params object[] args) + { + object result; + + try + { + result = engine.CallGlobalFunction(functionName, args); + } + catch (OriginalJavaScriptException e) + { + throw WrapJavaScriptException(engine, e); + } return result; } - protected override T InnerEvaluate(string expression) + /// + /// Gets a value of variable without converting it to a host type + /// + /// Original JS engine + /// Variable name + /// Value of variable not converted to a host type + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static object InnerGetVariableValueWithoutResultConversion(OriginalEngine engine, string variableName) { - object result = InnerEvaluate(expression); + object result; - return OriginalTypeConverter.ConvertTo(_jsEngine, result); + try + { + result = engine.GetGlobalValue(variableName); + } + catch (OriginalJavaScriptException e) + { + throw WrapJavaScriptException(engine, e); + } + + return result; } - protected override void InnerExecute(string code) + #region JsEngineBase overrides + + protected override IPrecompiledScript InnerPrecompile(string code) { + return InnerPrecompile(code, null); + } + + protected override IPrecompiledScript InnerPrecompile(string code, string documentName) + { + OriginalCompiledScript compiledScript; + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + lock (_executionSynchronizer) { try { - _jsEngine.Execute(code); - } - catch (OriginalJsException e) - { - throw ConvertJavascriptExceptionToJsRuntimeException(e); + var source = new OriginalStringScriptSource(code, uniqueDocumentName); + compiledScript = OriginalCompiledScript.Compile(source); } - catch (NotImplementedException e) + catch (OriginalSyntaxException e) { - throw new JsRuntimeException(e.Message, e); + throw WrapSyntaxException(e); } } + + return new JurassicPrecompiledScript(compiledScript); } - protected override object InnerCallFunction(string functionName, params object[] args) + protected override object InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override object InnerEvaluate(string expression, string documentName) { - int argumentCount = args.Length; - var processedArgs = new object[argumentCount]; + object resultValue; + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); - if (argumentCount > 0) + lock (_executionSynchronizer) { - for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) - { - processedArgs[argumentIndex] = MapToJurassicType(args[argumentIndex]); - } + resultValue = InnerEvaluateWithoutResultConversion(_jsEngine, expression, uniqueDocumentName); } - object result; + object result = MapToHostType(resultValue); + + return result; + } + + protected override T InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override T InnerEvaluate(string expression, string documentName) + { + T result; + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + + lock (_executionSynchronizer) + { + object resultValue = InnerEvaluateWithoutResultConversion(_jsEngine, expression, uniqueDocumentName); + result = MapToHostType(_jsEngine, resultValue); + } + + return result; + } + + protected override void InnerExecute(string code) + { + InnerExecute(code, null); + } + + protected override void InnerExecute(string code, string documentName) + { + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); lock (_executionSynchronizer) { try { - result = _jsEngine.CallGlobalFunction(functionName, processedArgs); + var source = new OriginalStringScriptSource(code, uniqueDocumentName); + _jsEngine.Execute(source); } - catch (OriginalJsException e) + catch (OriginalJavaScriptException e) { - throw ConvertJavascriptExceptionToJsRuntimeException(e); + throw WrapJavaScriptException(_jsEngine, e); } - catch (NotImplementedException e) + } + } + + protected override void InnerExecute(IPrecompiledScript precompiledScript) + { + var jurassicPrecompiledScript = precompiledScript as JurassicPrecompiledScript; + if (jurassicPrecompiledScript == null) + { + throw new WrapperUsageException( + string.Format(CoreStrings.Usage_CannotConvertPrecompiledScriptToInternalType, + typeof(JurassicPrecompiledScript).FullName), + Name, Version + ); + } + + lock (_executionSynchronizer) + { + try { - throw new JsRuntimeException(e.Message, e); + jurassicPrecompiledScript.CompiledScript.Execute(_jsEngine); } + catch (OriginalJavaScriptException e) + { + throw WrapJavaScriptException(_jsEngine, e); + } + } + } + + protected override object InnerCallFunction(string functionName, params object[] args) + { + object resultValue; + object[] processedArgs = MapToScriptType(args); + + lock (_executionSynchronizer) + { + resultValue = InnerCallFunctionWithoutResultConversion(_jsEngine, functionName, processedArgs); } - result = MapToHostType(result); + object result = MapToHostType(resultValue); return result; } protected override T InnerCallFunction(string functionName, params object[] args) { - object result = InnerCallFunction(functionName, args); + T result; + object[] processedArgs = MapToScriptType(args); + + lock (_executionSynchronizer) + { + object resultValue = InnerCallFunctionWithoutResultConversion(_jsEngine, functionName, processedArgs); + result = MapToHostType(_jsEngine, resultValue); + } - return OriginalTypeConverter.ConvertTo(_jsEngine, result); + return result; } protected override bool InnerHasVariable(string variableName) @@ -270,35 +568,34 @@ protected override bool InnerHasVariable(string variableName) protected override object InnerGetVariableValue(string variableName) { - object result; + object resultValue; lock (_executionSynchronizer) { - try - { - result = _jsEngine.GetGlobalValue(variableName); - } - catch (OriginalJsException e) - { - throw ConvertJavascriptExceptionToJsRuntimeException(e); - } + resultValue = InnerGetVariableValueWithoutResultConversion(_jsEngine, variableName); } - result = MapToHostType(result); + object result = MapToHostType(resultValue); return result; } protected override T InnerGetVariableValue(string variableName) { - object result = InnerGetVariableValue(variableName); + T result; - return OriginalTypeConverter.ConvertTo(_jsEngine, result); + lock (_executionSynchronizer) + { + object resultValue = InnerGetVariableValueWithoutResultConversion(_jsEngine, variableName); + result = MapToHostType(_jsEngine, resultValue); + } + + return result; } protected override void InnerSetVariableValue(string variableName, object value) { - object processedValue = MapToJurassicType(value); + object processedValue = MapToScriptType(value); lock (_executionSynchronizer) { @@ -306,9 +603,9 @@ protected override void InnerSetVariableValue(string variableName, object value) { _jsEngine.SetGlobalValue(variableName, processedValue); } - catch (OriginalJsException e) + catch (OriginalJavaScriptException e) { - throw ConvertJavascriptExceptionToJsRuntimeException(e); + throw WrapJavaScriptException(_jsEngine, e); } } } @@ -320,7 +617,7 @@ protected override void InnerRemoveVariable(string variableName) protected override void InnerEmbedHostObject(string itemName, object value) { - object processedValue = MapToJurassicType(value); + object processedValue = MapToScriptType(value); lock (_executionSynchronizer) { @@ -336,9 +633,9 @@ protected override void InnerEmbedHostObject(string itemName, object value) _jsEngine.SetGlobalValue(itemName, processedValue); } } - catch (OriginalJsException e) + catch (OriginalJavaScriptException e) { - throw ConvertJavascriptExceptionToJsRuntimeException(e); + throw WrapJavaScriptException(_jsEngine, e); } } } @@ -351,38 +648,391 @@ protected override void InnerEmbedHostType(string itemName, Type type) { _jsEngine.SetGlobalValue(itemName, type); } - catch (OriginalJsException e) + catch (OriginalJavaScriptException e) { - throw ConvertJavascriptExceptionToJsRuntimeException(e); + throw WrapJavaScriptException(_jsEngine, e); } } } + protected override void InnerInterrupt() + { + throw new NotSupportedException(); + } + + protected override void InnerCollectGarbage() + { + throw new NotSupportedException(); + } + + #region IJsEngine implementation + + public override string Name + { + get { return EngineName; } + } + + public override string Version + { + get { return EngineVersion; } + } + + public override bool SupportsScriptPrecompilation + { + get { return true; } + } + + public override bool SupportsScriptInterruption + { + get { return false; } + } + + public override bool SupportsGarbageCollection + { + get { return false; } + } + + + public override IPrecompiledScript PrecompileFile(string path, Encoding encoding = null) + { + VerifyNotDisposed(); + + if (path == null) + { + throw new ArgumentNullException( + nameof(path), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(path)) + ); + } + + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(path)), + nameof(path) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(path)) + { + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidFileNameFormat, path), + nameof(path) + ); + } + + OriginalCompiledScript compiledScript; + string uniqueDocumentName = _documentNameManager.GetUniqueName(path); + + lock (_executionSynchronizer) + { + try + { + var source = new FileScriptSource(uniqueDocumentName, path, encoding); + compiledScript = OriginalCompiledScript.Compile(source); + } + catch (OriginalSyntaxException e) + { + throw WrapSyntaxException(e); + } + catch (FileNotFoundException) + { + throw; + } + } + + return new JurassicPrecompiledScript(compiledScript); + } + + public override IPrecompiledScript PrecompileResource(string resourceName, Type type) + { + VerifyNotDisposed(); + + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (type == null) + { + throw new ArgumentNullException( + nameof(type), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(type)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); + } + +#if NET40 + Assembly assembly = type.Assembly; +#else + Assembly assembly = type.GetTypeInfo().Assembly; +#endif + string nameSpace = type.Namespace; + string resourceFullName = nameSpace != null ? nameSpace + "." + resourceName : resourceName; + + OriginalCompiledScript compiledScript; + string uniqueDocumentName = _documentNameManager.GetUniqueName(resourceFullName); + + lock (_executionSynchronizer) + { + try + { + var source = new ResourceScriptSource(uniqueDocumentName, resourceFullName, assembly); + compiledScript = OriginalCompiledScript.Compile(source); + } + catch (OriginalSyntaxException e) + { + throw WrapSyntaxException(e); + } + catch (NullReferenceException) + { + throw; + } + } + + return new JurassicPrecompiledScript(compiledScript); + } + + public override IPrecompiledScript PrecompileResource(string resourceName, Assembly assembly) + { + VerifyNotDisposed(); + + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (assembly == null) + { + throw new ArgumentNullException( + nameof(assembly), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(assembly)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); + } + + OriginalCompiledScript compiledScript; + string uniqueDocumentName = _documentNameManager.GetUniqueName(resourceName); + + lock (_executionSynchronizer) + { + try + { + var source = new ResourceScriptSource(uniqueDocumentName, resourceName, assembly); + compiledScript = OriginalCompiledScript.Compile(source); + } + catch (OriginalSyntaxException e) + { + throw WrapSyntaxException(e); + } + catch (NullReferenceException) + { + throw; + } + } + + return new JurassicPrecompiledScript(compiledScript); + } + public override void ExecuteFile(string path, Encoding encoding = null) { VerifyNotDisposed(); + if (path == null) + { + throw new ArgumentNullException( + nameof(path), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(path)) + ); + } + if (string.IsNullOrWhiteSpace(path)) { throw new ArgumentException( - string.Format(CoreStrings.Common_ArgumentIsEmpty, "path"), "path"); + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(path)), + nameof(path) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(path)) + { + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidFileNameFormat, path), + nameof(path) + ); + } + + string uniqueDocumentName = _documentNameManager.GetUniqueName(path); + + lock (_executionSynchronizer) + { + try + { + var source = new FileScriptSource(uniqueDocumentName, path, encoding); + _jsEngine.Execute(source); + } + catch (OriginalJavaScriptException e) + { + throw WrapJavaScriptException(_jsEngine, e); + } + catch (FileNotFoundException) + { + throw; + } + } + } + + public override void ExecuteResource(string resourceName, Type type) + { + VerifyNotDisposed(); + + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (type == null) + { + throw new ArgumentNullException( + nameof(type), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(type)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); } - if (!File.Exists(path)) + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) { - throw new FileNotFoundException( - string.Format(CoreStrings.Common_FileNotExist, path), path); + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); } +#if NET40 + Assembly assembly = type.Assembly; +#else + Assembly assembly = type.GetTypeInfo().Assembly; +#endif + string nameSpace = type.Namespace; + string resourceFullName = nameSpace != null ? nameSpace + "." + resourceName : resourceName; + string uniqueDocumentName = _documentNameManager.GetUniqueName(resourceFullName); + lock (_executionSynchronizer) { try { - _jsEngine.ExecuteFile(path, encoding); + var source = new ResourceScriptSource(uniqueDocumentName, resourceFullName, assembly); + _jsEngine.Execute(source); + } + catch (OriginalJavaScriptException e) + { + throw WrapJavaScriptException(_jsEngine, e); } - catch (OriginalJsException e) + catch (NullReferenceException) { - throw ConvertJavascriptExceptionToJsRuntimeException(e); + throw; + } + } + } + + public override void ExecuteResource(string resourceName, Assembly assembly) + { + VerifyNotDisposed(); + + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (assembly == null) + { + throw new ArgumentNullException( + nameof(assembly), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(assembly)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); + } + + string uniqueDocumentName = _documentNameManager.GetUniqueName(resourceName); + + lock (_executionSynchronizer) + { + try + { + var source = new ResourceScriptSource(uniqueDocumentName, resourceName, assembly); + _jsEngine.Execute(source); + } + catch (OriginalJavaScriptException e) + { + throw WrapJavaScriptException(_jsEngine, e); + } + catch (NullReferenceException) + { + throw; } } } @@ -403,5 +1053,7 @@ public override void Dispose() } #endregion + + #endregion } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/JurassicJsEngineFactory.cs b/src/JavaScriptEngineSwitcher.Jurassic/JurassicJsEngineFactory.cs new file mode 100644 index 00000000..dc4053c2 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jurassic/JurassicJsEngineFactory.cs @@ -0,0 +1,53 @@ +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Jurassic +{ + /// + /// Jurassic JS engine factory + /// + public sealed class JurassicJsEngineFactory : IJsEngineFactory + { + /// + /// Settings of the Jurassic JS engine + /// + private readonly JurassicSettings _settings; + + + /// + /// Constructs an instance of the Jurassic JS engine factory + /// + public JurassicJsEngineFactory() + : this(new JurassicSettings()) + { } + + /// + /// Constructs an instance of the Jurassic JS engine factory + /// + /// Settings of the Jurassic JS engine + public JurassicJsEngineFactory(JurassicSettings settings) + { + _settings = settings; + } + + + #region IJsEngineFactory implementation + + /// + public string EngineName + { + get { return JurassicJsEngine.EngineName; } + } + + + /// + /// Creates a instance of the Jurassic JS engine + /// + /// Instance of the Jurassic JS engine + public IJsEngine CreateEngine() + { + return new JurassicJsEngine(_settings); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/JurassicPrecompiledScript.cs b/src/JavaScriptEngineSwitcher.Jurassic/JurassicPrecompiledScript.cs new file mode 100644 index 00000000..0983ae64 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jurassic/JurassicPrecompiledScript.cs @@ -0,0 +1,42 @@ +using OriginalCompiledScript = Jurassic.CompiledScript; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Jurassic +{ + /// + /// Represents a pre-compiled script that can be executed by different instances of the Jurassic JS engine + /// + internal sealed class JurassicPrecompiledScript : IPrecompiledScript + { + /// + /// Gets a compiled script + /// + public OriginalCompiledScript CompiledScript + { + get; + private set; + } + + + /// + /// Constructs an instance of pre-compiled script + /// + /// The compiled script + public JurassicPrecompiledScript(OriginalCompiledScript compiledScript) + { + CompiledScript = compiledScript; + } + + + #region IPrecompiledScript implementation + + /// + public string EngineName + { + get { return JurassicJsEngine.EngineName; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/JurassicSettings.cs b/src/JavaScriptEngineSwitcher.Jurassic/JurassicSettings.cs new file mode 100644 index 00000000..a2964472 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jurassic/JurassicSettings.cs @@ -0,0 +1,68 @@ +using System; + +namespace JavaScriptEngineSwitcher.Jurassic +{ + /// + /// Settings of the Jurassic JS engine + /// + public sealed class JurassicSettings + { + /// + /// Gets or sets a flag for whether to enable conversion of host collections, + /// that are passed or returned to script code, to script arrays + /// + /// + /// This property does not allow the embedding of host collections by + /// using a + /// method, it only affects the internal mechanisms of the Jurassic library. + /// + public bool EnableHostCollectionsEmbeddingByValue + { + get; + set; + } +#if !NETSTANDARD2_0 + + /// + /// Gets or sets a flag for whether to enable script debugging features + /// (allows a generation of debug information) + /// + [Obsolete("Since the Jurassic version 3.2.1, debugging is no longer supported.")] + public bool EnableDebugging + { + get; + set; + } +#endif + + /// + /// Gets or sets a flag for whether to disassemble any generated IL + /// and store it in the associated function + /// + public bool EnableIlAnalysis + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to allow run the script in strict mode + /// + public bool StrictMode + { + get; + set; + } + + + /// + /// Constructs an instance of the Jurassic settings + /// + public JurassicSettings() + { + EnableHostCollectionsEmbeddingByValue = false; + EnableIlAnalysis = false; + StrictMode = false; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.Jurassic/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..ad182c32 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jurassic/PACKAGE-DESCRIPTION.md @@ -0,0 +1 @@ +JavaScriptEngineSwitcher.Jurassic contains a `JurassicJsEngine` adapter (wrapper for the [Jurassic](http://github.com/paulbartrum/jurassic) version of February 4, 2025). \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/Properties/AssemblyInfo.cs b/src/JavaScriptEngineSwitcher.Jurassic/Properties/AssemblyInfo.cs deleted file mode 100644 index 8853a6d1..00000000 --- a/src/JavaScriptEngineSwitcher.Jurassic/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("JavaScriptEngineSwitcher.Jurassic")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JavaScript Engine Switcher for .Net: Jurassic")] -[assembly: AssemblyCopyright("Copyright © 2013-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("1e4eb77e-0771-4109-832f-20e3475315ed")] - -[assembly: AssemblyVersion("1.5.8.0")] -[assembly: AssemblyFileVersion("1.5.8.0")] \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/ResourceScriptSource.cs b/src/JavaScriptEngineSwitcher.Jurassic/ResourceScriptSource.cs new file mode 100644 index 00000000..b295b27b --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jurassic/ResourceScriptSource.cs @@ -0,0 +1,117 @@ +using System; +using System.IO; +using System.Reflection; + +using OriginalScriptSource = Jurassic.ScriptSource; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; + +namespace JavaScriptEngineSwitcher.Jurassic +{ + /// + /// Represents a embedded JS resource + /// + internal sealed class ResourceScriptSource : OriginalScriptSource + { + /// + /// The document name + /// + private readonly string _documentName; + + /// + /// The case-sensitive resource name + /// + private readonly string _resourceName; + + /// + /// The assembly, which contains the embedded resource + /// + private readonly Assembly _assembly; + + + /// + /// Constructs an instance of + /// + /// The document name + /// The case-sensitive resource name + /// The assembly, which contains the embedded resource + public ResourceScriptSource(string documentName, string resourceName, Assembly assembly) + { + if (documentName == null) + { + throw new ArgumentNullException( + nameof(documentName), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(documentName)) + ); + } + + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (assembly == null) + { + throw new ArgumentNullException( + nameof(assembly), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(assembly)) + ); + } + + if (string.IsNullOrWhiteSpace(documentName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(documentName)), + nameof(documentName) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + _documentName = documentName; + _resourceName = resourceName; + _assembly = assembly; + } + + + #region Jurassic.ScriptSource overrides + + /// + /// Gets a document name + /// + public override string Path + { + get { return _documentName; } + } + + + /// + /// Gets a reader that can be used to read the source code from the embedded JS resource + /// + /// The reader that can be used to read the source code from the embedded + /// JS resource, positioned at the start of the source code + public override TextReader GetReader() + { + Stream stream = _assembly.GetManifestResourceStream(_resourceName); + + if (stream == null) + { + throw new NullReferenceException( + string.Format(CoreStrings.Common_ResourceIsNull, _resourceName)); + } + + return new StreamReader(stream); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jurassic/readme.txt b/src/JavaScriptEngineSwitcher.Jurassic/readme.txt new file mode 100644 index 00000000..88a9730b --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Jurassic/readme.txt @@ -0,0 +1,27 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: Jurassic v3.29.0 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2025 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + JavaScriptEngineSwitcher.Jurassic contains a `JurassicJsEngine` adapter (wrapper + for the Jurassic (http://github.com/paulbartrum/jurassic) version of + February 4, 2025). + + ============= + RELEASE NOTES + ============= + Jurassic was updated to version of February 4, 2025. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/Configuration/MsieConfiguration.cs b/src/JavaScriptEngineSwitcher.Msie/Configuration/MsieConfiguration.cs deleted file mode 100644 index 8392a034..00000000 --- a/src/JavaScriptEngineSwitcher.Msie/Configuration/MsieConfiguration.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace JavaScriptEngineSwitcher.Msie.Configuration -{ - using System.Configuration; - - /// - /// Configuration settings of MSIE JavaScript engine - /// - public sealed class MsieConfiguration : ConfigurationSection - { - /// - /// Gets or sets a flag for whether to enable script debugging features - /// (only works in the ChakraIeJsRt and ChakraEdgeJsRt modes) - /// - [ConfigurationProperty("enableDebugging", DefaultValue = false)] - public bool EnableDebugging - { - get { return (bool)this["enableDebugging"]; } - set { this["enableDebugging"] = value; } - } - - /// - /// Gets or sets a JavaScript engine mode - /// - [ConfigurationProperty("engineMode", DefaultValue = JsEngineMode.Auto)] - public JsEngineMode EngineMode - { - get { return (JsEngineMode)this["engineMode"]; } - set { this["engineMode"] = value; } - } - - /// - /// Gets or sets a flag for whether to use the ECMAScript 5 Polyfill - /// - [ConfigurationProperty("useEcmaScript5Polyfill", DefaultValue = false)] - public bool UseEcmaScript5Polyfill - { - get { return (bool)this["useEcmaScript5Polyfill"]; } - set { this["useEcmaScript5Polyfill"] = value; } - } - - /// - /// Gets or sets a flag for whether to use the JSON2 library - /// - [ConfigurationProperty("useJson2Library", DefaultValue = false)] - public bool UseJson2Library - { - get { return (bool)this["useJson2Library"]; } - set { this["useJson2Library"] = value; } - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/JavaScriptEngineSwitcher.Msie.csproj b/src/JavaScriptEngineSwitcher.Msie/JavaScriptEngineSwitcher.Msie.csproj index 235cb3a2..bfe69608 100644 --- a/src/JavaScriptEngineSwitcher.Msie/JavaScriptEngineSwitcher.Msie.csproj +++ b/src/JavaScriptEngineSwitcher.Msie/JavaScriptEngineSwitcher.Msie.csproj @@ -1,84 +1,44 @@ - - - + + - Debug - AnyCPU - {50AD3B1C-A295-42AC-979A-CD244429983C} + JS Engine Switcher: MSIE + 3.24.1 + net40-client;net45;netstandard1.3;netstandard2.0 + 1.6.0 Library - Properties - JavaScriptEngineSwitcher.Msie - JavaScriptEngineSwitcher.Msie - v4.0 - 512 - ..\..\ - true - Client - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + true + $(NoWarn);CS1591;NETSDK1215;NU1903 + false + true + true + + + + + + - true + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Msie_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_Msie_Logo128x128.png + JavaScriptEngineSwitcher.Msie contains a `MsieJsEngine` adapter (wrapper for the MSIE JavaScript Engine for .NET). + $(PackageCommonTags);MSIE;IE;Chakra + MSIE JavaScript Engine was updated to version 3.2.5. - - ..\..\JavaScriptEngineSwitcher.snk - - - - {5c903eef-bad1-43b8-bfe2-e4ee4d204410} - JavaScriptEngineSwitcher.Core - - - - - ..\..\packages\MsieJavaScriptEngine.1.7.1\lib\net40-client\MsieJavaScriptEngine.dll - True - - - - - + - - - - - + + + + + - - JavaScriptEngineSwitcher.snk - - + - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/JsEngineFactoryCollectionExtensions.cs b/src/JavaScriptEngineSwitcher.Msie/JsEngineFactoryCollectionExtensions.cs new file mode 100644 index 00000000..dd5d4910 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Msie/JsEngineFactoryCollectionExtensions.cs @@ -0,0 +1,79 @@ +using System; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Msie +{ + /// + /// JS engine factory collection extensions + /// + public static class JsEngineFactoryCollectionExtensions + { + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Instance of + public static JsEngineFactoryCollection AddMsie(this JsEngineFactoryCollection source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return source.AddMsie(new MsieSettings()); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The delegate to configure the provided + /// Instance of + public static JsEngineFactoryCollection AddMsie(this JsEngineFactoryCollection source, + Action configure) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var settings = new MsieSettings(); + configure(settings); + + return source.AddMsie(settings); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Settings of the MSIE JS engine + /// Instance of + public static JsEngineFactoryCollection AddMsie(this JsEngineFactoryCollection source, + MsieSettings settings) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + source.Add(new MsieJsEngineFactory(settings)); + + return source; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/JsEngineMode.cs b/src/JavaScriptEngineSwitcher.Msie/JsEngineMode.cs index df73226e..11d38f0d 100644 --- a/src/JavaScriptEngineSwitcher.Msie/JsEngineMode.cs +++ b/src/JavaScriptEngineSwitcher.Msie/JsEngineMode.cs @@ -1,38 +1,39 @@ namespace JavaScriptEngineSwitcher.Msie { /// - /// JavaScript engine modes + /// MSIE JS engine modes /// public enum JsEngineMode { /// - /// Automatically selects the most modern JavaScript engine from available on the machine + /// Automatically selects the most modern JS engine from available on the machine /// Auto = 0, /// - /// Classic MSIE JavaScript engine (supports ECMAScript 3 with - /// possibility of using the ECMAScript 5 Polyfill and the JSON2 library). + /// Classic MSIE JS engine (supports ECMAScript 3 with possibility of using + /// the ECMAScript 5 Polyfill and the JSON2 library). /// Requires Internet Explorer 6 or higher on the machine. + /// Not supported in version for .NET Core. /// Classic, /// - /// ActiveScript version of Chakra JavaScript engine (supports ECMAScript 3 - /// with possibility of using the ECMAScript 5 Polyfill and the JSON2 library). + /// ActiveScript version of Chakra JS engine (supports ECMAScript 5). /// Requires Internet Explorer 9 or higher on the machine. + /// Not supported in version for .NET Core. /// ChakraActiveScript, /// - /// “IE” JsRT version of Chakra JavaScript engine (supports ECMAScript 5). - /// Requires Internet Explorer 11 or Microsoft Edge on the machine. + /// “IE” JsRT version of Chakra JS engine (supports ECMAScript 5). + /// Requires Internet Explorer 11 or Microsoft Edge Legacy on the machine. /// ChakraIeJsRt, /// - /// “Edge” JsRT version of Chakra JavaScript engine (supports ECMAScript 5). - /// Requires Microsoft Edge on the machine. + /// “Edge” JsRT version of Chakra JS engine (supports ECMAScript 5). + /// Requires Microsoft Edge Legacy on the machine. /// ChakraEdgeJsRt } diff --git a/src/JavaScriptEngineSwitcher.Msie/JsEngineSwitcherExtensions.cs b/src/JavaScriptEngineSwitcher.Msie/JsEngineSwitcherExtensions.cs deleted file mode 100644 index cc4f649c..00000000 --- a/src/JavaScriptEngineSwitcher.Msie/JsEngineSwitcherExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace JavaScriptEngineSwitcher.Msie -{ - using System; - using System.Configuration; - - using Core; - using Configuration; - - /// - /// JavaScript engine switcher extensions - /// - public static class JsEngineSwitcherExtensions - { - /// - /// Configuration settings of MSIE JavaScript engine - /// - private static readonly Lazy _msieConfig = - new Lazy(() => (MsieConfiguration)ConfigurationManager.GetSection("jsEngineSwitcher/msie")); - - /// - /// Gets a MSIE JavaScript engine configuration settings - /// - /// JavaScript engine switcher> - /// Configuration settings of MSIE JavaScript engine - public static MsieConfiguration GetMsieConfiguration(this JsEngineSwitcher switcher) - { - return _msieConfig.Value; - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/MsieJsEngine.cs b/src/JavaScriptEngineSwitcher.Msie/MsieJsEngine.cs index 34385402..0e438370 100644 --- a/src/JavaScriptEngineSwitcher.Msie/MsieJsEngine.cs +++ b/src/JavaScriptEngineSwitcher.Msie/MsieJsEngine.cs @@ -1,106 +1,112 @@ -namespace JavaScriptEngineSwitcher.Msie +using System; +using System.IO; +using System.Reflection; +using System.Text; + +using OriginalCompilationException = MsieJavaScriptEngine.JsCompilationException; +using OriginalEngine = MsieJavaScriptEngine.MsieJsEngine; +using OriginalEngineException = MsieJavaScriptEngine.JsEngineException; +using OriginalEngineLoadException = MsieJavaScriptEngine.JsEngineLoadException; +using OriginalEngineMode = MsieJavaScriptEngine.JsEngineMode; +using OriginalEngineSettings = MsieJavaScriptEngine.JsEngineSettings; +using OriginalException = MsieJavaScriptEngine.JsException; +using OriginalFatalException = MsieJavaScriptEngine.JsFatalException; +using OriginalInterruptedException = MsieJavaScriptEngine.JsInterruptedException; +using OriginalPrecompiledScript = MsieJavaScriptEngine.PrecompiledScript; +using OriginalRuntimeException = MsieJavaScriptEngine.JsRuntimeException; +using OriginalScriptException = MsieJavaScriptEngine.JsScriptException; +using OriginalTypeConverter = MsieJavaScriptEngine.Utilities.TypeConverter; +using OriginalUndefined = MsieJavaScriptEngine.Undefined; +using OriginalUsageException = MsieJavaScriptEngine.JsUsageException; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Helpers; +using JavaScriptEngineSwitcher.Core.Utilities; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; +using WrapperCompilationException = JavaScriptEngineSwitcher.Core.JsCompilationException; +using WrapperEngineException = JavaScriptEngineSwitcher.Core.JsEngineException; +using WrapperEngineLoadException = JavaScriptEngineSwitcher.Core.JsEngineLoadException; +using WrapperException = JavaScriptEngineSwitcher.Core.JsException; +using WrapperFatalException = JavaScriptEngineSwitcher.Core.JsFatalException; +using WrapperInterruptedException = JavaScriptEngineSwitcher.Core.JsInterruptedException; +using WrapperRuntimeException = JavaScriptEngineSwitcher.Core.JsRuntimeException; +using WrapperScriptException = JavaScriptEngineSwitcher.Core.JsScriptException; +using WrapperUsageException = JavaScriptEngineSwitcher.Core.JsUsageException; + +namespace JavaScriptEngineSwitcher.Msie { - using System; - - using OriginalJsEngine = MsieJavaScriptEngine.MsieJsEngine; - using OriginalJsEngineLoadException = MsieJavaScriptEngine.JsEngineLoadException; - using OriginalJsEngineMode = MsieJavaScriptEngine.JsEngineMode; - using OriginalJsRuntimeException = MsieJavaScriptEngine.JsRuntimeException; - using OriginalJsEngineSettings = MsieJavaScriptEngine.JsEngineSettings; - using OriginalTypeConverter = MsieJavaScriptEngine.Utilities.TypeConverter; - using OriginalUndefined = MsieJavaScriptEngine.Undefined; - - using Core; - using Core.Utilities; - using CoreStrings = Core.Resources.Strings; - - using Configuration; - /// - /// Adapter for MSIE JavaScript engine + /// Adapter for the MSIE JS engine /// public sealed class MsieJsEngine : JsEngineBase { /// - /// Name of JavaScript engine + /// Name of JS engine /// - private const string ENGINE_NAME = "MSIE JavaScript engine"; + public const string EngineName = "MsieJsEngine"; /// - /// Version of original JavaScript engine + /// Version of original JS engine /// private readonly string _engineVersion; /// /// MSIE JS engine /// - private OriginalJsEngine _jsEngine; + private OriginalEngine _jsEngine; - /// - /// Gets a name of JavaScript engine - /// - public override string Name - { - get { return ENGINE_NAME; } - } /// - /// Gets a version of original JavaScript engine - /// - public override string Version - { - get { return _engineVersion; } - } - - - /// - /// Constructs a instance of adapter for MSIE JavaScript engine + /// Constructs an instance of adapter for the MSIE JS engine /// public MsieJsEngine() - : this(JsEngineSwitcher.Current.GetMsieConfiguration()) + : this(new MsieSettings()) { } /// - /// Constructs a instance of adapter for MSIE JavaScript engine + /// Constructs an instance of adapter for the MSIE JS engine /// - /// Configuration settings of MSIE JavaScript engine - public MsieJsEngine(MsieConfiguration config) + /// Settings of the MSIE JS engine + public MsieJsEngine(MsieSettings settings) { - MsieConfiguration msieConfig = config ?? new MsieConfiguration(); + MsieSettings msieSettings = settings ?? new MsieSettings(); try { - _jsEngine = new OriginalJsEngine(new OriginalJsEngineSettings + _jsEngine = new OriginalEngine(new OriginalEngineSettings { - EnableDebugging = msieConfig.EnableDebugging, - EngineMode = Utils.GetEnumFromOtherEnum( - msieConfig.EngineMode), - UseEcmaScript5Polyfill = msieConfig.UseEcmaScript5Polyfill, - UseJson2Library = msieConfig.UseJson2Library + AllowReflection = msieSettings.AllowReflection, + EnableDebugging = msieSettings.EnableDebugging, + EngineMode = Utils.GetEnumFromOtherEnum( + msieSettings.EngineMode), +#if !NETSTANDARD1_3 + MaxStackSize = msieSettings.MaxStackSize, +#endif + UseEcmaScript5Polyfill = msieSettings.UseEcmaScript5Polyfill, + UseJson2Library = msieSettings.UseJson2Library }); _engineVersion = _jsEngine.Mode; } - catch (OriginalJsEngineLoadException e) + catch (OriginalUsageException e) { - throw new JsEngineLoadException( - string.Format(CoreStrings.Runtime_JsEngineNotLoaded, - ENGINE_NAME, e.Message), ENGINE_NAME, e.EngineMode, e); + throw JsErrorHelpers.WrapEngineLoadException(e, EngineName, _engineVersion); } - catch (Exception e) + catch (OriginalException e) { - throw new JsEngineLoadException( - string.Format(CoreStrings.Runtime_JsEngineNotLoaded, - ENGINE_NAME, e.Message), ENGINE_NAME, _engineVersion, e); + throw WrapJsException(e); } } + #region Mapping + /// - /// Executes a mapping from the host type to a MSIE type + /// Makes a mapping of value from the host type to a script type /// /// The source value /// The mapped value - private static object MapToMsieType(object value) + private static object MapToScriptType(object value) { if (value is Undefined) { @@ -111,7 +117,7 @@ private static object MapToMsieType(object value) } /// - /// Executes a mapping from the MSIE type to a host type + /// Makes a mapping of value from the script type to a host type /// /// The source value /// The mapped value @@ -125,37 +131,130 @@ private static object MapToHostType(object value) return value; } - private JsRuntimeException ConvertMsieJsRuntimeExceptionToJsRuntimeException( - OriginalJsRuntimeException msieJsRuntimeException) + private WrapperException WrapJsException(OriginalException originalException) { - var jsRuntimeException = new JsRuntimeException(msieJsRuntimeException.Message, - ENGINE_NAME, _engineVersion, msieJsRuntimeException) + WrapperException wrapperException; + + var originalScriptException = originalException as OriginalScriptException; + if (originalScriptException != null) { - ErrorCode = msieJsRuntimeException.ErrorCode, - Category = msieJsRuntimeException.Category, - LineNumber = msieJsRuntimeException.LineNumber, - ColumnNumber = msieJsRuntimeException.ColumnNumber, - SourceFragment = msieJsRuntimeException.SourceFragment, - Source = msieJsRuntimeException.Source, - HelpLink = msieJsRuntimeException.HelpLink - }; + WrapperScriptException wrapperScriptException; - return jsRuntimeException; + var originalRuntimeException = originalScriptException as OriginalRuntimeException; + if (originalRuntimeException != null) + { + WrapperRuntimeException wrapperRuntimeException; + if (originalRuntimeException is OriginalInterruptedException) + { + wrapperRuntimeException = new WrapperInterruptedException(originalScriptException.Message, + EngineName, _engineVersion, originalRuntimeException); + } + else + { + wrapperRuntimeException = new WrapperRuntimeException(originalScriptException.Message, + EngineName, _engineVersion, originalRuntimeException); + } + wrapperRuntimeException.CallStack = originalRuntimeException.CallStack; + + wrapperScriptException = wrapperRuntimeException; + } + else if (originalScriptException is OriginalCompilationException) + { + wrapperScriptException = new WrapperCompilationException(originalScriptException.Message, + EngineName, _engineVersion, originalScriptException); + } + else + { + wrapperScriptException = new WrapperScriptException(originalScriptException.Message, + EngineName, _engineVersion, originalScriptException); + } + + wrapperScriptException.Type = originalScriptException.Type; + wrapperScriptException.DocumentName = originalScriptException.DocumentName; + wrapperScriptException.LineNumber = originalScriptException.LineNumber; + wrapperScriptException.ColumnNumber = originalScriptException.ColumnNumber; + wrapperScriptException.SourceFragment = originalScriptException.SourceFragment; + + wrapperException = wrapperScriptException; + } + else + { + if (originalException is OriginalUsageException) + { + wrapperException = new WrapperUsageException(originalException.Message, + EngineName, _engineVersion, originalException); + } + else if (originalException is OriginalEngineException) + { + if (originalException is OriginalEngineLoadException) + { + wrapperException = new WrapperEngineLoadException(originalException.Message, + EngineName, _engineVersion, originalException); + } + else + { + wrapperException = new WrapperEngineException(originalException.Message, + EngineName, _engineVersion, originalException); + } + } + else if (originalException is OriginalFatalException) + { + wrapperException = new WrapperFatalException(originalException.Message, + EngineName, _engineVersion, originalException); + } + else + { + wrapperException = new WrapperException(originalException.Message, + EngineName, _engineVersion, originalException); + } + } + + wrapperException.Description = originalException.Description; + + return wrapperException; + } + + #endregion + + #region JsEngineBase overrides + + protected override IPrecompiledScript InnerPrecompile(string code) + { + return InnerPrecompile(code, null); } - #region JsEngineBase implementation + protected override IPrecompiledScript InnerPrecompile(string code, string documentName) + { + OriginalPrecompiledScript precompiledScript; + + try + { + precompiledScript = _jsEngine.Precompile(code, documentName); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + + return new MsiePrecompiledScript(precompiledScript); + } protected override object InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override object InnerEvaluate(string expression, string documentName) { object result; try { - result = _jsEngine.Evaluate(expression); + result = _jsEngine.Evaluate(expression, documentName); } - catch (OriginalJsRuntimeException e) + catch (OriginalException e) { - throw ConvertMsieJsRuntimeExceptionToJsRuntimeException(e); + throw WrapJsException(e); } result = MapToHostType(result); @@ -165,20 +264,52 @@ protected override object InnerEvaluate(string expression) protected override T InnerEvaluate(string expression) { - object result = InnerEvaluate(expression); + return InnerEvaluate(expression, null); + } + + protected override T InnerEvaluate(string expression, string documentName) + { + object result = InnerEvaluate(expression, documentName); return OriginalTypeConverter.ConvertToType(result); } protected override void InnerExecute(string code) + { + InnerExecute(code, null); + } + + protected override void InnerExecute(string code, string documentName) { try { - _jsEngine.Execute(code); + _jsEngine.Execute(code, documentName); } - catch (OriginalJsRuntimeException e) + catch (OriginalException e) { - throw ConvertMsieJsRuntimeExceptionToJsRuntimeException(e); + throw WrapJsException(e); + } + } + + protected override void InnerExecute(IPrecompiledScript precompiledScript) + { + var msiePrecompiledScript = precompiledScript as MsiePrecompiledScript; + if (msiePrecompiledScript == null) + { + throw new WrapperUsageException( + string.Format(CoreStrings.Usage_CannotConvertPrecompiledScriptToInternalType, + typeof(MsiePrecompiledScript).FullName), + Name, Version + ); + } + + try + { + _jsEngine.Execute(msiePrecompiledScript.PrecompiledScript); + } + catch (OriginalException e) + { + throw WrapJsException(e); } } @@ -192,7 +323,7 @@ protected override object InnerCallFunction(string functionName, params object[] { for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) { - processedArgs[argumentIndex] = MapToMsieType(args[argumentIndex]); + processedArgs[argumentIndex] = MapToScriptType(args[argumentIndex]); } } @@ -200,9 +331,9 @@ protected override object InnerCallFunction(string functionName, params object[] { result = _jsEngine.CallFunction(functionName, processedArgs); } - catch (OriginalJsRuntimeException e) + catch (OriginalException e) { - throw ConvertMsieJsRuntimeExceptionToJsRuntimeException(e); + throw WrapJsException(e); } result = MapToHostType(result); @@ -225,9 +356,9 @@ protected override bool InnerHasVariable(string variableName) { result = _jsEngine.HasVariable(variableName); } - catch (OriginalJsRuntimeException e) + catch (OriginalException e) { - throw ConvertMsieJsRuntimeExceptionToJsRuntimeException(e); + throw WrapJsException(e); } return result; @@ -241,9 +372,9 @@ protected override object InnerGetVariableValue(string variableName) { result = _jsEngine.GetVariableValue(variableName); } - catch (OriginalJsRuntimeException e) + catch (OriginalException e) { - throw ConvertMsieJsRuntimeExceptionToJsRuntimeException(e); + throw WrapJsException(e); } result = MapToHostType(result); @@ -260,15 +391,15 @@ protected override T InnerGetVariableValue(string variableName) protected override void InnerSetVariableValue(string variableName, object value) { - object processedValue = MapToMsieType(value); + object processedValue = MapToScriptType(value); try { _jsEngine.SetVariableValue(variableName, processedValue); } - catch (OriginalJsRuntimeException e) + catch (OriginalException e) { - throw ConvertMsieJsRuntimeExceptionToJsRuntimeException(e); + throw WrapJsException(e); } } @@ -278,23 +409,23 @@ protected override void InnerRemoveVariable(string variableName) { _jsEngine.RemoveVariable(variableName); } - catch (OriginalJsRuntimeException e) + catch (OriginalException e) { - throw ConvertMsieJsRuntimeExceptionToJsRuntimeException(e); + throw WrapJsException(e); } } protected override void InnerEmbedHostObject(string itemName, object value) { - object processedValue = MapToMsieType(value); + object processedValue = MapToScriptType(value); try { _jsEngine.EmbedHostObject(itemName, processedValue); } - catch (OriginalJsRuntimeException e) + catch (OriginalException e) { - throw ConvertMsieJsRuntimeExceptionToJsRuntimeException(e); + throw WrapJsException(e); } } @@ -304,9 +435,343 @@ protected override void InnerEmbedHostType(string itemName, Type type) { _jsEngine.EmbedHostType(itemName, type); } - catch (OriginalJsRuntimeException e) + catch (OriginalException e) { - throw ConvertMsieJsRuntimeExceptionToJsRuntimeException(e); + throw WrapJsException(e); + } + } + + protected override void InnerInterrupt() + { + _jsEngine.Interrupt(); + } + + protected override void InnerCollectGarbage() + { + _jsEngine.CollectGarbage(); + } + + #region IJsEngine implementation + + public override string Name + { + get { return EngineName; } + } + + public override string Version + { + get { return _engineVersion; } + } + + public override bool SupportsScriptPrecompilation + { + get { return _jsEngine.SupportsScriptPrecompilation; } + } + + public override bool SupportsScriptInterruption + { + get { return true; } + } + + public override bool SupportsGarbageCollection + { + get { return true; } + } + + + public override IPrecompiledScript PrecompileFile(string path, Encoding encoding = null) + { + VerifyNotDisposed(); + + if (path == null) + { + throw new ArgumentNullException( + nameof(path), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(path)) + ); + } + + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(path)), + nameof(path) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(path)) + { + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidFileNameFormat, path), + nameof(path) + ); + } + + OriginalPrecompiledScript precompiledScript; + + try + { + precompiledScript = _jsEngine.PrecompileFile(path, encoding); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + catch (FileNotFoundException) + { + throw; + } + + return new MsiePrecompiledScript(precompiledScript); + } + + public override IPrecompiledScript PrecompileResource(string resourceName, Type type) + { + VerifyNotDisposed(); + + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (type == null) + { + throw new ArgumentNullException( + nameof(type), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(type)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); + } + + OriginalPrecompiledScript precompiledScript; + + try + { + precompiledScript = _jsEngine.PrecompileResource(resourceName, type); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + catch (NullReferenceException) + { + throw; + } + + return new MsiePrecompiledScript(precompiledScript); + } + + public override IPrecompiledScript PrecompileResource(string resourceName, Assembly assembly) + { + VerifyNotDisposed(); + + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (assembly == null) + { + throw new ArgumentNullException( + nameof(assembly), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(assembly)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); + } + + OriginalPrecompiledScript precompiledScript; + + try + { + precompiledScript = _jsEngine.PrecompileResource(resourceName, assembly); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + catch (NullReferenceException) + { + throw; + } + + return new MsiePrecompiledScript(precompiledScript); + } + + public override void ExecuteFile(string path, Encoding encoding = null) + { + VerifyNotDisposed(); + + if (path == null) + { + throw new ArgumentNullException( + nameof(path), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(path)) + ); + } + + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(path)), + nameof(path) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(path)) + { + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidFileNameFormat, path), + nameof(path) + ); + } + + try + { + _jsEngine.ExecuteFile(path, encoding); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + catch (FileNotFoundException) + { + throw; + } + } + + public override void ExecuteResource(string resourceName, Type type) + { + VerifyNotDisposed(); + + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (type == null) + { + throw new ArgumentNullException( + nameof(type), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(type)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); + } + + try + { + _jsEngine.ExecuteResource(resourceName, type); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + catch (NullReferenceException) + { + throw; + } + } + + public override void ExecuteResource(string resourceName, Assembly assembly) + { + VerifyNotDisposed(); + + if (resourceName == null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (assembly == null) + { + throw new ArgumentNullException( + nameof(assembly), + string.Format(CoreStrings.Common_ArgumentIsNull, nameof(assembly)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(CoreStrings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); + } + + try + { + _jsEngine.ExecuteResource(resourceName, assembly); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + catch (NullReferenceException) + { + throw; } } @@ -327,5 +792,7 @@ public override void Dispose() } #endregion + + #endregion } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/MsieJsEngineFactory.cs b/src/JavaScriptEngineSwitcher.Msie/MsieJsEngineFactory.cs new file mode 100644 index 00000000..15b32429 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Msie/MsieJsEngineFactory.cs @@ -0,0 +1,53 @@ +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Msie +{ + /// + /// MSIE JS engine factory + /// + public sealed class MsieJsEngineFactory : IJsEngineFactory + { + /// + /// Settings of the MSIE JS engine + /// + private readonly MsieSettings _settings; + + + /// + /// Constructs an instance of the MSIE JS engine factory + /// + public MsieJsEngineFactory() + : this(new MsieSettings()) + { } + + /// + /// Constructs an instance of the MSIE JS engine factory + /// + /// Settings of the MSIE JS engine + public MsieJsEngineFactory(MsieSettings settings) + { + _settings = settings; + } + + + #region IJsEngineFactory implementation + + /// + public string EngineName + { + get { return MsieJsEngine.EngineName; } + } + + + /// + /// Creates a instance of the MSIE JS engine + /// + /// Instance of the MSIE JS engine + public IJsEngine CreateEngine() + { + return new MsieJsEngine(_settings); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/MsiePrecompiledScript.cs b/src/JavaScriptEngineSwitcher.Msie/MsiePrecompiledScript.cs new file mode 100644 index 00000000..5004f43e --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Msie/MsiePrecompiledScript.cs @@ -0,0 +1,42 @@ +using OriginalPrecompiledScript = MsieJavaScriptEngine.PrecompiledScript; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Msie +{ + /// + /// Represents a pre-compiled script that can be executed by different instances of the MSIE JS engine + /// + internal sealed class MsiePrecompiledScript : IPrecompiledScript + { + /// + /// Gets a original pre-compiled script + /// + public OriginalPrecompiledScript PrecompiledScript + { + get; + private set; + } + + + /// + /// Constructs an instance of pre-compiled script + /// + /// The original pre-compiled script + public MsiePrecompiledScript(OriginalPrecompiledScript precompiledScript) + { + PrecompiledScript = precompiledScript; + } + + + #region IPrecompiledScript implementation + + /// + public string EngineName + { + get { return MsieJsEngine.EngineName; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/MsieSettings.cs b/src/JavaScriptEngineSwitcher.Msie/MsieSettings.cs new file mode 100644 index 00000000..a5e85290 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Msie/MsieSettings.cs @@ -0,0 +1,122 @@ +using System; + +using JavaScriptEngineSwitcher.Core.Utilities; + +using JavaScriptEngineSwitcher.Msie.Resources; + +namespace JavaScriptEngineSwitcher.Msie +{ + /// + /// Settings of the MSIE JS engine + /// + public sealed class MsieSettings + { +#if !NETSTANDARD1_3 + /// + /// The stack size is sufficient to run the code of modern JavaScript libraries in 32-bit process + /// + const int STACK_SIZE_32 = 492 * 1024; // like 32-bit Node.js + + /// + /// The stack size is sufficient to run the code of modern JavaScript libraries in 64-bit process + /// + const int STACK_SIZE_64 = 984 * 1024; // like 64-bit Node.js + + /// + /// The maximum stack size in bytes + /// + private int _maxStackSize; + +#endif + /// + /// Gets or sets a flag for whether to allow the usage of reflection API in the script code + /// + /// + /// This affects , Exception.GetType, + /// Exception.TargetSite and Delegate.Method. + /// + public bool AllowReflection + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to enable script debugging features + /// + public bool EnableDebugging + { + get; + set; + } + + /// + /// Gets or sets a JS engine mode + /// + public JsEngineMode EngineMode + { + get; + set; + } +#if !NETSTANDARD1_3 + + /// + /// Gets or sets a maximum stack size in bytes + /// + /// + /// Set a 0 to use the default maximum stack size specified in the header + /// for the executable. + /// + public int MaxStackSize + { + get { return _maxStackSize; } + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException( + nameof(value), + Strings.Engine_MaxStackSizeMustBeNonNegative + ); + } + + _maxStackSize = value; + } + } +#endif + + /// + /// Gets or sets a flag for whether to use the ECMAScript 5 Polyfill + /// + public bool UseEcmaScript5Polyfill + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to use the JSON2 library + /// + public bool UseJson2Library + { + get; + set; + } + + + /// + /// Constructs an instance of the MSIE settings + /// + public MsieSettings() + { + AllowReflection = false; + EnableDebugging = false; + EngineMode = JsEngineMode.Auto; +#if !NETSTANDARD1_3 + MaxStackSize = Utils.Is64BitProcess() ? STACK_SIZE_64 : STACK_SIZE_32; +#endif + UseEcmaScript5Polyfill = false; + UseJson2Library = false; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.Msie/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..c0017681 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Msie/PACKAGE-DESCRIPTION.md @@ -0,0 +1,3 @@ +JavaScriptEngineSwitcher.Msie contains a `MsieJsEngine` adapter (wrapper for the [MSIE JavaScript Engine for .NET](http://github.com/Taritsyn/MsieJavaScriptEngine)). + +For correct working of the MSIE JavaScript Engine it is recommended to install Internet Explorer 9+ or Microsoft Edge Legacy on the machine. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/Properties/AssemblyInfo.cs b/src/JavaScriptEngineSwitcher.Msie/Properties/AssemblyInfo.cs deleted file mode 100644 index 3bbd3bd1..00000000 --- a/src/JavaScriptEngineSwitcher.Msie/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("JavaScriptEngineSwitcher.Msie")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JavaScript Engine Switcher for .Net: MSIE")] -[assembly: AssemblyCopyright("Copyright © 2013-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("1c5512ac-5d2f-45dc-92d4-725624a0016c")] - -[assembly: AssemblyVersion("1.5.4.0")] -[assembly: AssemblyFileVersion("1.5.4.0")] \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/Resources/Strings.Designer.cs b/src/JavaScriptEngineSwitcher.Msie/Resources/Strings.Designer.cs new file mode 100644 index 00000000..50649830 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Msie/Resources/Strings.Designer.cs @@ -0,0 +1,75 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +namespace JavaScriptEngineSwitcher.Msie.Resources +{ + using System; + using System.Globalization; + using System.Reflection; + using System.Resources; + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + internal class Strings + { + private static Lazy _resourceManager = + new Lazy(() => new ResourceManager( + "JavaScriptEngineSwitcher.Msie.Resources.Strings", +#if NET20 || NET30 || NET35 || NET40 + typeof(Strings).Assembly +#else + typeof(Strings).GetTypeInfo().Assembly +#endif + )); + + private static CultureInfo _resourceCulture; + + /// + /// Returns a cached ResourceManager instance used by this class + /// + internal static ResourceManager ResourceManager + { + get + { + return _resourceManager.Value; + } + } + + /// + /// Overrides a current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class + /// + internal static CultureInfo Culture + { + get + { + return _resourceCulture; + } + set + { + _resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to "Maximum stack size must be non-negative." + /// + internal static string Engine_MaxStackSizeMustBeNonNegative + { + get { return GetString("Engine_MaxStackSizeMustBeNonNegative"); } + } + + private static string GetString(string name) + { + string value = ResourceManager.GetString(name, _resourceCulture); + + return value; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/Resources/Strings.ru-ru.resx b/src/JavaScriptEngineSwitcher.Msie/Resources/Strings.resx similarity index 91% rename from src/JavaScriptEngineSwitcher.Jint/Resources/Strings.ru-ru.resx rename to src/JavaScriptEngineSwitcher.Msie/Resources/Strings.resx index 17712e80..192ed132 100644 --- a/src/JavaScriptEngineSwitcher.Jint/Resources/Strings.ru-ru.resx +++ b/src/JavaScriptEngineSwitcher.Msie/Resources/Strings.resx @@ -117,13 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Превышено максимальное время выполнения скрипта. - - - Рекурсия запрещена скриптовым хостом: {0} - - - Превышено максимальное количество выполняемых инструкций. + + Maximum stack size must be non-negative. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Jint/Resources/Strings.ru-ru.Designer.cs b/src/JavaScriptEngineSwitcher.Msie/Resources/Strings.ru-RU.Designer.cs similarity index 100% rename from src/JavaScriptEngineSwitcher.Jint/Resources/Strings.ru-ru.Designer.cs rename to src/JavaScriptEngineSwitcher.Msie/Resources/Strings.ru-RU.Designer.cs diff --git a/src/JavaScriptEngineSwitcher.Msie/Resources/Strings.ru-RU.resx b/src/JavaScriptEngineSwitcher.Msie/Resources/Strings.ru-RU.resx new file mode 100644 index 00000000..25c1edbf --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Msie/Resources/Strings.ru-RU.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Максимальный размер стека должен быть неотрицательным! + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/packages.config b/src/JavaScriptEngineSwitcher.Msie/packages.config deleted file mode 100644 index feee281f..00000000 --- a/src/JavaScriptEngineSwitcher.Msie/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Msie/readme.txt b/src/JavaScriptEngineSwitcher.Msie/readme.txt new file mode 100644 index 00000000..2f82f48b --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Msie/readme.txt @@ -0,0 +1,28 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: MSIE v3.24.1 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + JavaScriptEngineSwitcher.Msie contains a `MsieJsEngine` adapter (wrapper for the + MSIE JavaScript Engine for .NET (http://github.com/Taritsyn/MsieJavaScriptEngine)). + For correct working of the MSIE JavaScript Engine it is recommended to install + Internet Explorer 9+ or Microsoft Edge Legacy on the machine. + + ============= + RELEASE NOTES + ============= + MSIE JavaScript Engine was updated to version 3.2.5. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.NiL/Helpers/NiLJsErrorHelpers.cs b/src/JavaScriptEngineSwitcher.NiL/Helpers/NiLJsErrorHelpers.cs new file mode 100644 index 00000000..91474ef0 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.NiL/Helpers/NiLJsErrorHelpers.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.RegularExpressions; + +using JavaScriptEngineSwitcher.Core.Extensions; +using JavaScriptEngineSwitcher.Core.Helpers; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; + +namespace JavaScriptEngineSwitcher.NiL.Helpers +{ + /// + /// JS error helpers + /// + internal static class NiLJsErrorHelpers + { + #region Error location + + private const string AtLinePrefix = " at "; + private const string DotNetStackTraceLinePrefix = AtLinePrefix + "NiL.JS."; + + private const string OriginalGlobalCode = "anonymous"; + private const string OriginalAnonymousFunctionName = ""; + private const string WrapperGlobalCode = "Global code"; + private const string WrapperAnonymousFunctionName = "Anonymous function"; + + /// + /// Regular expression for working with line of the script error location + /// + private static readonly Regex _errorLocationLineRegex = + new Regex(@"^" + AtLinePrefix + + @"(?" + + @"[\w][\w ]*" + + @"|" + + CommonRegExps.JsFullNamePattern + + @"|" + + Regex.Escape(OriginalAnonymousFunctionName) + + @")" + + @"(?::line (?\d+):(?\d+))?$"); + + + /// + /// Parses a string representation of the script error location to produce an array of + /// instances + /// + /// String representation of the script error location + /// An array of instances + public static ErrorLocationItem[] ParseErrorLocation(string errorLocation) + { + if (string.IsNullOrWhiteSpace(errorLocation)) + { + return new ErrorLocationItem[0]; + } + + var errorLocationItems = new List(); + string[] lines = errorLocation.SplitToLines(); + int lineCount = lines.Length; + + for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) + { + string line = lines[lineIndex]; + + if (line.Length == 0) + { + continue; + } + + // Completing parsing when a .NET stack trace is found + if (line.StartsWith(DotNetStackTraceLinePrefix, StringComparison.Ordinal)) + { + break; + } + + Match lineMatch = _errorLocationLineRegex.Match(line); + if (lineMatch.Success) + { + GroupCollection lineGroups = lineMatch.Groups; + Group lineNumberGroup = lineGroups["lineNumber"]; + Group columnNumberGroup = lineGroups["columnNumber"]; + + var errorLocationItem = new ErrorLocationItem + { + FunctionName = lineGroups["functionName"].Value, + LineNumber = lineNumberGroup.Success ? int.Parse(lineNumberGroup.Value) : 0, + ColumnNumber = columnNumberGroup.Success ? int.Parse(columnNumberGroup.Value) : 0, + }; + errorLocationItems.Add(errorLocationItem); + } + else + { + Debug.WriteLine(string.Format(CoreStrings.Runtime_InvalidErrorLocationLineFormat, line)); + return new ErrorLocationItem[0]; + } + } + + return errorLocationItems.ToArray(); + } + + /// + /// Fixes a error location items + /// + /// An array of instances + public static void FixErrorLocationItems(ErrorLocationItem[] errorLocationItems) + { + foreach (ErrorLocationItem errorLocationItem in errorLocationItems) + { + string functionName = errorLocationItem.FunctionName; + if (functionName.Length == 0 || functionName == OriginalGlobalCode) + { + errorLocationItem.FunctionName = WrapperGlobalCode; + } + else if (functionName == OriginalAnonymousFunctionName) + { + errorLocationItem.FunctionName = WrapperAnonymousFunctionName; + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.NiL/JavaScriptEngineSwitcher.NiL.csproj b/src/JavaScriptEngineSwitcher.NiL/JavaScriptEngineSwitcher.NiL.csproj new file mode 100644 index 00000000..6c41ccdd --- /dev/null +++ b/src/JavaScriptEngineSwitcher.NiL/JavaScriptEngineSwitcher.NiL.csproj @@ -0,0 +1,37 @@ + + + + JS Engine Switcher: NiL + 3.30.0 + net461;net48;netcoreapp3.1;net6.0;net8.0;net9.0 + Library + true + $(NoWarn);CS1591 + false + true + false + + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_NiL_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_NiL_Logo128x128.png + JavaScriptEngineSwitcher.NiL contains a `NiLJsEngine` adapter (wrapper for the NiL.JS). + $(PackageCommonTags);NiL + NiL.JS was updated to version 2.6.1712. + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.NiL/JsEngineFactoryCollectionExtensions.cs b/src/JavaScriptEngineSwitcher.NiL/JsEngineFactoryCollectionExtensions.cs new file mode 100644 index 00000000..985d6ef5 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.NiL/JsEngineFactoryCollectionExtensions.cs @@ -0,0 +1,78 @@ +using System; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.NiL +{ + /// + /// JS engine factory collection extensions + /// + public static class JsEngineFactoryCollectionExtensions + { + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Instance of + public static JsEngineFactoryCollection AddNiL(this JsEngineFactoryCollection source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return source.AddNiL(new NiLSettings()); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The delegate to configure the provided + /// Instance of + public static JsEngineFactoryCollection AddNiL(this JsEngineFactoryCollection source, + Action configure) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var settings = new NiLSettings(); + configure(settings); + + return source.AddNiL(settings); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Settings of the NiL JS engine + /// Instance of + public static JsEngineFactoryCollection AddNiL(this JsEngineFactoryCollection source, NiLSettings settings) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + source.Add(new NiLJsEngineFactory(settings)); + + return source; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.NiL/NiLJsEngine.cs b/src/JavaScriptEngineSwitcher.NiL/NiLJsEngine.cs new file mode 100644 index 00000000..90405344 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.NiL/NiLJsEngine.cs @@ -0,0 +1,588 @@ +using System; +using System.Text.RegularExpressions; + +using NiL.JS.Extensions; + +using OriginalArguments = NiL.JS.Core.Arguments; +using OriginalCodeCoordinates = NiL.JS.Core.CodeCoordinates; +using OriginalContext = NiL.JS.Core.Context; +using OriginalDebuggerCallback = NiL.JS.Core.DebuggerCallback; +using OriginalError = NiL.JS.BaseLibrary.Error; +using OriginalException = NiL.JS.Core.JSException; +using OriginalFunction = NiL.JS.BaseLibrary.Function; +using OriginalValue = NiL.JS.Core.JSValue; +using OriginalValueType = NiL.JS.Core.JSValueType; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Constants; +using JavaScriptEngineSwitcher.Core.Helpers; +using JavaScriptEngineSwitcher.Core.Utilities; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; +using WrapperCompilationException = JavaScriptEngineSwitcher.Core.JsCompilationException; +using WrapperException = JavaScriptEngineSwitcher.Core.JsException; +using WrapperRuntimeException = JavaScriptEngineSwitcher.Core.JsRuntimeException; +using WrapperScriptException = JavaScriptEngineSwitcher.Core.JsScriptException; + +using JavaScriptEngineSwitcher.NiL.Helpers; + +namespace JavaScriptEngineSwitcher.NiL +{ + /// + /// Adapter for the NiL JS engine + /// + public sealed class NiLJsEngine : JsEngineBase + { + /// + /// Name of JS engine + /// + public const string EngineName = "NiLJsEngine"; + + /// + /// Version of original JS engine + /// + private const string EngineVersion = "2.6.1712"; + + /// + /// Regular expression for working with the syntax error message + /// + private static readonly Regex _syntaxErrorMessageRegex = + new Regex(@"^(?[\s\S]+?) (?:at )?\((?\d+):(?\d+)\)$"); + + /// + /// NiL JS context + /// + private OriginalContext _jsContext; + + /// + /// Debugger callback + /// + private OriginalDebuggerCallback _debuggerCallback; + + /// + /// Synchronizer + /// + private readonly object _synchronizer = new object(); + + + /// + /// Constructs an instance of adapter for the NiL JS engine + /// + public NiLJsEngine() + : this(new NiLSettings()) + { } + + /// + /// Constructs an instance of adapter for the NiL JS engine + /// + /// Settings of the NiL JS engine + public NiLJsEngine(NiLSettings settings) + { + NiLSettings niLSettings = settings ?? new NiLSettings(); + _debuggerCallback = niLSettings.DebuggerCallback; + + try + { + _jsContext = new OriginalContext(niLSettings.StrictMode); + _jsContext.Debugging = niLSettings.EnableDebugging; + if (_debuggerCallback != null) + { + _jsContext.DebuggerCallback += _debuggerCallback; + } + _jsContext.GlobalContext.CurrentTimeZone = niLSettings.LocalTimeZone; + } + catch (Exception e) + { + throw JsErrorHelpers.WrapEngineLoadException(e, EngineName, EngineVersion, true); + } + } + + #region Mapping + + /// + /// Makes a mapping of value from the host type to a script type + /// + /// The source value + /// The mapped value + private static OriginalValue MapToScriptType(object value) + { + if (value == null) + { + return OriginalValue.Null; + } + + if (value is Undefined) + { + return OriginalValue.Undefined; + } + + return OriginalContext.CurrentGlobalContext.ProxyValue(value); + } + + /// + /// Makes a mapping of value from the script type to a host type + /// + /// The source value + /// The mapped value + private static object MapToHostType(OriginalValue value) + { + if (value.IsNull) + { + return null; + } + + OriginalValueType valueType = value.ValueType; + object result; + + switch (valueType) + { + case OriginalValueType.NotExists: + case OriginalValueType.NotExistsInObject: + case OriginalValueType.Undefined: + result = Undefined.Value; + break; + + case OriginalValueType.Boolean: + case OriginalValueType.Integer: + case OriginalValueType.Double: + case OriginalValueType.String: + case OriginalValueType.Symbol: + case OriginalValueType.Object: + case OriginalValueType.Function: + case OriginalValueType.Date: + case OriginalValueType.Property: + case OriginalValueType.SpreadOperatorResult: + result = value.Value; + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + return result; + } + + private static WrapperException WrapJsException(OriginalException originalException) + { + WrapperException wrapperException; + string message = originalException.Message; + string description = message; + string type = string.Empty; + int lineNumber = 0; + int columnNumber = 0; + string sourceFragment = string.Empty; + + var errorValue = originalException.Error?.Value as OriginalError; + if (errorValue != null) + { + message = errorValue.message.As(); + description = message; + type = errorValue.name.As(); + } + + if (!string.IsNullOrEmpty(type)) + { + WrapperScriptException wrapperScriptException; + if (type == JsErrorType.Syntax) + { + Match messageMatch = _syntaxErrorMessageRegex.Match(message); + if (messageMatch.Success) + { + GroupCollection messageGroups = messageMatch.Groups; + description = messageGroups["description"].Value; + lineNumber = int.Parse(messageGroups["lineNumber"].Value); + columnNumber = int.Parse(messageGroups["columnNumber"].Value); + } + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, string.Empty, + lineNumber, columnNumber); + + wrapperScriptException = new WrapperCompilationException(message, EngineName, EngineVersion, + originalException); + } + else + { + string sourceCode = originalException.SourceCode; + OriginalCodeCoordinates codeCoordinates = originalException.CodeCoordinates; + if (codeCoordinates != null) + { + lineNumber = codeCoordinates.Line; + columnNumber = codeCoordinates.Column; + } + + sourceFragment = TextHelpers.GetTextFragment(sourceCode, lineNumber, columnNumber); + string callStack = string.Empty; + ErrorLocationItem[] callStackItems = NiLJsErrorHelpers.ParseErrorLocation( + originalException.StackTrace); + if (callStackItems.Length > 0) + { + NiLJsErrorHelpers.FixErrorLocationItems(callStackItems); + + ErrorLocationItem firstCallStackItem = callStackItems[0]; + firstCallStackItem.SourceFragment = sourceFragment; + + callStack = JsErrorHelpers.StringifyErrorLocationItems(callStackItems, true); + string callStackWithSourceFragment = JsErrorHelpers.StringifyErrorLocationItems( + callStackItems); + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, + callStackWithSourceFragment); + } + else + { + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, string.Empty, + lineNumber, columnNumber, sourceFragment); + } + + var wrapperRuntimeException = new WrapperRuntimeException(message, EngineName, EngineVersion, + originalException); + wrapperRuntimeException.CallStack = callStack; + + wrapperScriptException = wrapperRuntimeException; + } + wrapperScriptException.Type = type; + wrapperScriptException.LineNumber = lineNumber; + wrapperScriptException.ColumnNumber = columnNumber; + wrapperScriptException.SourceFragment = sourceFragment; + + wrapperException = wrapperScriptException; + } + else + { + wrapperException = new WrapperException(message, EngineName, EngineVersion, originalException); + } + + wrapperException.Description = description; + + return wrapperException; + } + + #endregion + + #region JsEngineBase overrides + + protected override IPrecompiledScript InnerPrecompile(string code) + { + throw new NotSupportedException(); + } + + protected override IPrecompiledScript InnerPrecompile(string code, string documentName) + { + throw new NotSupportedException(); + } + + protected override object InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override object InnerEvaluate(string expression, string documentName) + { + OriginalValue resultValue; + + try + { + lock (_synchronizer) + { + resultValue = _jsContext.Eval(expression, true); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + + object result = MapToHostType(resultValue); + + return result; + } + + protected override T InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override T InnerEvaluate(string expression, string documentName) + { + object result = InnerEvaluate(expression, documentName); + + return TypeConverter.ConvertToType(result); + } + + protected override void InnerExecute(string code) + { + InnerExecute(code, null); + } + + protected override void InnerExecute(string code, string documentName) + { + try + { + lock (_synchronizer) + { + _jsContext.Eval(code, true); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + + protected override void InnerExecute(IPrecompiledScript precompiledScript) + { + throw new NotSupportedException(); + } + + protected override object InnerCallFunction(string functionName, params object[] args) + { + OriginalValue resultValue; + var processedArgs = new OriginalArguments(); + + int argumentCount = args.Length; + if (argumentCount > 0) + { + for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) + { + processedArgs.Add(MapToScriptType(args[argumentIndex])); + } + } + + try + { + lock (_synchronizer) + { + OriginalValue functionValue = _jsContext.GetVariable(functionName); + var function = functionValue.As(); + if (function == null) + { + throw new WrapperRuntimeException( + string.Format(CoreStrings.Runtime_FunctionNotExist, functionName)); + } + + resultValue = function.Call(processedArgs); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + catch (WrapperRuntimeException) + { + throw; + } + + object result = MapToHostType(resultValue); + + return result; + } + + protected override T InnerCallFunction(string functionName, params object[] args) + { + object result = InnerCallFunction(functionName, args); + + return TypeConverter.ConvertToType(result); + } + + protected override bool InnerHasVariable(string variableName) + { + bool result; + + lock (_synchronizer) + { + try + { + OriginalValue variableValue = _jsContext.GetVariable(variableName); + OriginalValueType valueType = variableValue.ValueType; + + result = valueType != OriginalValueType.NotExists && valueType != OriginalValueType.Undefined; + } + catch (OriginalException) + { + result = false; + } + } + + return result; + } + + protected override object InnerGetVariableValue(string variableName) + { + object result; + + try + { + lock (_synchronizer) + { + OriginalValue variableValue = _jsContext.GetVariable(variableName); + if (variableValue.ValueType == OriginalValueType.NotExists) + { + throw new WrapperRuntimeException( + string.Format(CoreStrings.Runtime_VariableNotExist, variableName), + EngineName, EngineVersion + ); + } + + result = MapToHostType(variableValue); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + + return result; + } + + protected override T InnerGetVariableValue(string variableName) + { + object result = InnerGetVariableValue(variableName); + + return TypeConverter.ConvertToType(result); + } + + protected override void InnerSetVariableValue(string variableName, object value) + { + OriginalValue processedValue = MapToScriptType(value); + + try + { + lock (_synchronizer) + { + OriginalValue variableValue = _jsContext.GetVariable(variableName); + if (variableValue.ValueType == OriginalValueType.NotExists) + { + variableValue = _jsContext.DefineVariable(variableName, true); + } + variableValue.Assign(processedValue); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + + protected override void InnerRemoveVariable(string variableName) + { + try + { + lock (_synchronizer) + { + _jsContext.DeleteVariable(variableName); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + + protected override void InnerEmbedHostObject(string itemName, object value) + { + OriginalValue processedValue = _jsContext.GlobalContext.ProxyValue(value); + + try + { + lock (_synchronizer) + { + OriginalValue variableValue = _jsContext.GetVariable(itemName); + if (variableValue.ValueType == OriginalValueType.NotExists) + { + variableValue = _jsContext.DefineVariable(itemName, true); + } + variableValue.Assign(processedValue); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + + protected override void InnerEmbedHostType(string itemName, Type type) + { + try + { + lock (_synchronizer) + { + OriginalValue processedValue = _jsContext.GlobalContext.GetConstructor(type); + + OriginalValue variableValue = _jsContext.GetVariable(itemName); + if (variableValue.ValueType == OriginalValueType.NotExists) + { + variableValue = _jsContext.DefineVariable(itemName, true); + } + variableValue.Assign(processedValue); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + + protected override void InnerInterrupt() + { + throw new NotSupportedException(); + } + + protected override void InnerCollectGarbage() + { + throw new NotSupportedException(); + } + + #region IJsEngine implementation + + public override string Name + { + get { return EngineName; } + } + + public override string Version + { + get { return EngineVersion; } + } + + public override bool SupportsScriptPrecompilation + { + get { return false; } + } + + public override bool SupportsScriptInterruption + { + get { return false; } + } + + public override bool SupportsGarbageCollection + { + get { return false; } + } + + #endregion + + #region IDisposable implementation + + public override void Dispose() + { + if (_disposedFlag.Set()) + { + lock (_synchronizer) + { + if (_jsContext != null) + { + if (_debuggerCallback != null) + { + _jsContext.DebuggerCallback -= _debuggerCallback; + _debuggerCallback = null; + } + + _jsContext = null; + } + } + } + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.NiL/NiLJsEngineFactory.cs b/src/JavaScriptEngineSwitcher.NiL/NiLJsEngineFactory.cs new file mode 100644 index 00000000..d4c51f48 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.NiL/NiLJsEngineFactory.cs @@ -0,0 +1,53 @@ +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.NiL +{ + /// + /// NiL JS engine factory + /// + public sealed class NiLJsEngineFactory : IJsEngineFactory + { + /// + /// Settings of the NiL JS engine + /// + private readonly NiLSettings _settings; + + + /// + /// Constructs an instance of the NiL JS engine factory + /// + public NiLJsEngineFactory() + : this(new NiLSettings()) + { } + + /// + /// Constructs an instance of the NiL JS engine factory + /// + /// Settings of the NiL JS engine + public NiLJsEngineFactory(NiLSettings settings) + { + _settings = settings; + } + + + #region IJsEngineFactory implementation + + /// + public string EngineName + { + get { return NiLJsEngine.EngineName; } + } + + + /// + /// Creates a instance of the NiL JS engine + /// + /// Instance of the NiL JS engine + public IJsEngine CreateEngine() + { + return new NiLJsEngine(_settings); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.NiL/NiLSettings.cs b/src/JavaScriptEngineSwitcher.NiL/NiLSettings.cs new file mode 100644 index 00000000..9fdd7142 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.NiL/NiLSettings.cs @@ -0,0 +1,60 @@ +using System; + +using OriginalDebuggerCallback = NiL.JS.Core.DebuggerCallback; + +namespace JavaScriptEngineSwitcher.NiL +{ + /// + /// Settings of the NiL JS engine + /// + public sealed class NiLSettings + { + /// + /// Gets or sets a debugger callback + /// + public OriginalDebuggerCallback DebuggerCallback + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to enable script debugging features + /// + public bool EnableDebugging + { + get; + set; + } + + /// + /// Gets or sets a local time zone for the Date objects in the script + /// + public TimeZoneInfo LocalTimeZone + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to allow run the script in strict mode + /// + public bool StrictMode + { + get; + set; + } + + + /// + /// Constructs an instance of the NiL settings + /// + public NiLSettings() + { + DebuggerCallback = null; + EnableDebugging = false; + LocalTimeZone = TimeZoneInfo.Local; + StrictMode = false; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.NiL/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.NiL/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..23fa1c6d --- /dev/null +++ b/src/JavaScriptEngineSwitcher.NiL/PACKAGE-DESCRIPTION.md @@ -0,0 +1 @@ +JavaScriptEngineSwitcher.NiL contains a `NiLJsEngine` adapter (wrapper for the [NiL.JS](https://github.com/nilproject/NiL.JS) version 2.6.1712). \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.NiL/readme.txt b/src/JavaScriptEngineSwitcher.NiL/readme.txt new file mode 100644 index 00000000..5e707cdc --- /dev/null +++ b/src/JavaScriptEngineSwitcher.NiL/readme.txt @@ -0,0 +1,26 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: NiL v3.30.0 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2025 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + JavaScriptEngineSwitcher.NiL contains a `NiLJsEngine` adapter (wrapper for the + NiL.JS (https://github.com/nilproject/NiL.JS) version 2.6.1712). + + ============= + RELEASE NOTES + ============= + NiL.JS was updated to version 2.6.1712. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Node/DefaultNodeJsService.cs b/src/JavaScriptEngineSwitcher.Node/DefaultNodeJsService.cs new file mode 100644 index 00000000..75efc139 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Node/DefaultNodeJsService.cs @@ -0,0 +1,117 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Jering.Javascript.NodeJS; + +namespace JavaScriptEngineSwitcher.Node +{ + /// + /// Default Node JS service + /// + /// + /// Wrapper around the class. + /// + public sealed class DefaultNodeJsService : INodeJSService + { + /// + /// Instance of default Node JS service + /// + private static readonly DefaultNodeJsService _instance = new DefaultNodeJsService(); + + /// + /// Gets a instance of default Node JS service + /// + public static INodeJSService Instance + { + get { return _instance; } + } + + + /// + /// Private constructor for implementation Singleton pattern + /// + private DefaultNodeJsService() + { } + + + #region INodeJSService implementation + + public Task InvokeFromFileAsync(string modulePath, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.InvokeFromFileAsync(modulePath, exportName, args, cancellationToken); + } + + public Task InvokeFromFileAsync(string modulePath, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.InvokeFromFileAsync(modulePath, exportName, args, cancellationToken); + } + + public Task InvokeFromStringAsync(string moduleString, string cacheIdentifier = null, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.InvokeFromStringAsync(moduleString, cacheIdentifier, exportName, args, cancellationToken); + } + + public Task InvokeFromStringAsync(string moduleString, string cacheIdentifier = null, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.InvokeFromStringAsync(moduleString, cacheIdentifier, exportName, args, cancellationToken); + } + + public Task InvokeFromStringAsync(Func moduleFactory, string cacheIdentifier, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.InvokeFromStringAsync(moduleFactory, cacheIdentifier, exportName, args, cancellationToken); + } + + public Task InvokeFromStringAsync(Func moduleFactory, string cacheIdentifier, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.InvokeFromStringAsync(moduleFactory, cacheIdentifier, exportName, args, cancellationToken); + } + + public Task InvokeFromStreamAsync(Stream moduleStream, string cacheIdentifier = null, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.InvokeFromStreamAsync(moduleStream, cacheIdentifier, exportName, args, cancellationToken); + } + + public Task InvokeFromStreamAsync(Stream moduleStream, string cacheIdentifier = null, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.InvokeFromStreamAsync(moduleStream, cacheIdentifier, exportName, args, cancellationToken); + } + + public Task InvokeFromStreamAsync(Func moduleFactory, string cacheIdentifier, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.InvokeFromStreamAsync(moduleFactory, cacheIdentifier, exportName, args, cancellationToken); + } + + public Task InvokeFromStreamAsync(Func moduleFactory, string cacheIdentifier, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.InvokeFromStreamAsync(moduleFactory, cacheIdentifier, exportName, args, cancellationToken); + } + + public Task<(bool, T)> TryInvokeFromCacheAsync(string moduleCacheIdentifier, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.TryInvokeFromCacheAsync(moduleCacheIdentifier, exportName, args, cancellationToken); + } + + public Task TryInvokeFromCacheAsync(string moduleCacheIdentifier, string exportName = null, object[] args = null, CancellationToken cancellationToken = default) + { + return StaticNodeJSService.TryInvokeFromCacheAsync(moduleCacheIdentifier, exportName, args, cancellationToken); + } + + public ValueTask MoveToNewProcessAsync() + { + throw new NotSupportedException(); + } + + #region IDisposable implementation + + public void Dispose() + { + throw new NotSupportedException(); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Node/Helpers/NodeJsErrorHelpers.cs b/src/JavaScriptEngineSwitcher.Node/Helpers/NodeJsErrorHelpers.cs new file mode 100644 index 00000000..4322ad75 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Node/Helpers/NodeJsErrorHelpers.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.RegularExpressions; + +using JavaScriptEngineSwitcher.Core.Extensions; +using JavaScriptEngineSwitcher.Core.Helpers; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; + +namespace JavaScriptEngineSwitcher.Node.Helpers +{ + /// + /// JS error helpers + /// + internal static class NodeJsErrorHelpers + { + #region Error location + + /// + /// Name of file, which identifies the generated function call + /// + private const string GeneratedFunctionCallDocumentName = "JavaScriptEngineSwitcher.Node.Resources.generated-function-call.js"; + + /// + /// Pattern for working with document names with coordinates + /// + private static readonly string DocumentNameWithCoordinatesPattern = + @"(?" + CommonRegExps.DocumentNamePattern + @"||\[eval\])" + + @"(?::(?\d+)(?::(?\d+))?)?"; + + /// + /// Pattern for working with JS function names + /// + private static readonly string JsFunctionNamePattern = CommonRegExps.JsNamePattern + + @"(?:\.(?:" + CommonRegExps.JsNamePattern + @"|))*"; + + /// + /// Regular expression for working with line of the script error location + /// + private static readonly Regex _errorLocationLineRegex = + new Regex(@"^[ ]{3,4}at " + + @"(?:" + + @"(?[\w][\w ]*|" + JsFunctionNamePattern + @") " + + @"\(" + DocumentNameWithCoordinatesPattern + @"\)" + + @"|" + + DocumentNameWithCoordinatesPattern + + @")" + + @"(?: -> (?[^\n\r]+))?$"); + + + /// + /// Parses a string representation of the script error location to produce an array of + /// instances + /// + /// String representation of the script error location + /// An array of instances + public static ErrorLocationItem[] ParseErrorLocation(string errorLocation) + { + if (string.IsNullOrWhiteSpace(errorLocation)) + { + return new ErrorLocationItem[0]; + } + + var errorLocationItems = new List(); + string[] lines = errorLocation.SplitToLines(); + int lineCount = lines.Length; + + for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) + { + string line = lines[lineIndex]; + if (line.Length == 0) + { + continue; + } + + Match lineMatch = _errorLocationLineRegex.Match(line); + + if (lineMatch.Success) + { + GroupCollection lineGroups = lineMatch.Groups; + Group lineNumberGroup = lineGroups["lineNumber"]; + Group columnNumberGroup = lineGroups["columnNumber"]; + + var errorLocationItem = new ErrorLocationItem + { + FunctionName = lineGroups["functionName"].Value, + DocumentName = lineGroups["documentName"].Value, + LineNumber = lineNumberGroup.Success ? int.Parse(lineNumberGroup.Value) : 0, + ColumnNumber = columnNumberGroup.Success ? int.Parse(columnNumberGroup.Value) : 0, + SourceFragment = lineGroups["sourceFragment"].Value + }; + errorLocationItems.Add(errorLocationItem); + } + else + { + Debug.WriteLine(string.Format(CoreStrings.Runtime_InvalidErrorLocationLineFormat, line)); + return new ErrorLocationItem[0]; + } + } + + return errorLocationItems.ToArray(); + } + + /// + /// Gets a column count from the text line + /// + /// Content of the text line + /// Column count from the text line + public static int GetColumnCountFromLine(string textLine) + { + if (string.IsNullOrEmpty(textLine)) + { + return 0; + } + + if (textLine.IndexOf('\t') == -1) + { + return textLine.Length; + } + + int charCount = textLine.Length; + int columnCount = 0; + + for (int charIndex = 0; charIndex < charCount; charIndex++) + { + char charValue = textLine[charIndex]; + int increment = charValue == '\t' ? 4 : 1; + + columnCount += increment; + } + + return columnCount; + } + + /// + /// Filters a error location items + /// + /// An array of instances + public static ErrorLocationItem[] FilterErrorLocationItems(ErrorLocationItem[] errorLocationItems) + { + int itemCount = errorLocationItems.Length; + if (itemCount == 0) + { + return errorLocationItems; + } + + int itemIndex = 0; + + while (itemIndex < itemCount) + { + ErrorLocationItem item = errorLocationItems[itemIndex]; + string documentName = item.DocumentName; + string functionName = item.FunctionName; + + if (documentName == "node:vm" + || documentName == "vm.js" + || documentName == GeneratedFunctionCallDocumentName + || (documentName == "anonymous" && functionName == "callFunction")) + { + break; + } + + itemIndex++; + } + + if (itemIndex == itemCount) + { + return errorLocationItems; + } + + var processedErrorLocationItems = new ErrorLocationItem[itemIndex]; + Array.Copy(errorLocationItems, processedErrorLocationItems, itemIndex); + + return processedErrorLocationItems; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Node/JavaScriptEngineSwitcher.Node.csproj b/src/JavaScriptEngineSwitcher.Node/JavaScriptEngineSwitcher.Node.csproj new file mode 100644 index 00000000..c8d22b0a --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Node/JavaScriptEngineSwitcher.Node.csproj @@ -0,0 +1,44 @@ + + + + JS Engine Switcher: Node + 3.24.1 + net461;netstandard2.0;netcoreapp3.1;net5.0;net6.0;net7.0 + Library + true + true + true + $(NoWarn);CS1591;NU1903 + false + true + true + false + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Node_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_Node_Logo128x128.png + JavaScriptEngineSwitcher.Node contains a `NodeJsEngine` adapter (wrapper for the Jering.Javascript.NodeJS). + $(PackageCommonTags);Node.js;Jering.Javascript.NodeJS + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Node/JsEngineFactoryCollectionExtensions.cs b/src/JavaScriptEngineSwitcher.Node/JsEngineFactoryCollectionExtensions.cs new file mode 100644 index 00000000..81d1b4be --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Node/JsEngineFactoryCollectionExtensions.cs @@ -0,0 +1,266 @@ +using System; + +using Jering.Javascript.NodeJS; +using Microsoft.Extensions.DependencyInjection; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Node +{ + /// + /// JS engine factory collection extensions + /// + public static class JsEngineFactoryCollectionExtensions + { + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Instance of + public static JsEngineFactoryCollection AddNode(this JsEngineFactoryCollection source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + source.Add(new NodeJsEngineFactory()); + + return source; + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Node JS service + /// Instance of + public static JsEngineFactoryCollection AddNode(this JsEngineFactoryCollection source, + INodeJSService service) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (service == null) + { + throw new ArgumentNullException(nameof(service)); + } + + source.Add(new NodeJsEngineFactory(service)); + + return source; + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The services available in the application + /// Instance of + public static JsEngineFactoryCollection AddNode(this JsEngineFactoryCollection source, + IServiceCollection services) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + source.Add(new NodeJsEngineFactory(services)); + + return source; + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The delegate to configure the provided + /// Instance of + public static JsEngineFactoryCollection AddNode(this JsEngineFactoryCollection source, + Action configure) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var settings = new NodeSettings(); + configure(settings); + + source.Add(new NodeJsEngineFactory(settings)); + + return source; + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Settings of the Node JS engine + /// Instance of + public static JsEngineFactoryCollection AddNode(this JsEngineFactoryCollection source, + NodeSettings settings) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + source.Add(new NodeJsEngineFactory(settings)); + + return source; + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Node JS service + /// The delegate to configure the provided + /// Instance of + public static JsEngineFactoryCollection AddNode(this JsEngineFactoryCollection source, + INodeJSService service, Action configure) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (service == null) + { + throw new ArgumentNullException(nameof(service)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var settings = new NodeSettings(); + configure(settings); + + source.Add(new NodeJsEngineFactory(service, settings)); + + return source; + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Node JS service + /// Settings of the Node JS engine + /// Instance of + public static JsEngineFactoryCollection AddNode(this JsEngineFactoryCollection source, + INodeJSService service, NodeSettings settings) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (service == null) + { + throw new ArgumentNullException(nameof(service)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + source.Add(new NodeJsEngineFactory(service, settings)); + + return source; + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The services available in the application + /// The delegate to configure the provided + /// Instance of + public static JsEngineFactoryCollection AddNode(this JsEngineFactoryCollection source, + IServiceCollection services, Action configure) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var settings = new NodeSettings(); + configure(settings); + + source.Add(new NodeJsEngineFactory(services, settings)); + + return source; + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The services available in the application + /// Settings of the Node JS engine + /// Instance of + public static JsEngineFactoryCollection AddNode(this JsEngineFactoryCollection source, + IServiceCollection services, NodeSettings settings) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + source.Add(new NodeJsEngineFactory(services, settings)); + + return source; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Node/JsEngineIdGenerator.cs b/src/JavaScriptEngineSwitcher.Node/JsEngineIdGenerator.cs new file mode 100644 index 00000000..3cb37a31 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Node/JsEngineIdGenerator.cs @@ -0,0 +1,44 @@ +using System; +using System.Threading; + +namespace JavaScriptEngineSwitcher.Node +{ + internal static class JsEngineIdGenerator + { + // Base32 encoding - in ascii sort order for easy text based sorting + private static readonly char[] _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV".ToCharArray(); + + // Seed the `_lastId` for this application instance with + // the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001 + // for a roughly increasing `_lastId` over restarts + private static long _lastId = DateTime.UtcNow.Ticks; + + + public static string GetNextId() => GenerateId(Interlocked.Increment(ref _lastId)); + + private static unsafe string GenerateId(long id) + { + char[] encode32Chars = _encode32Chars; + + // stackalloc to allocate array on stack rather than heap + char* buffer = stackalloc char[13]; + + buffer[12] = encode32Chars[id & 31]; + buffer[11] = encode32Chars[(id >> 5) & 31]; + buffer[10] = encode32Chars[(id >> 10) & 31]; + buffer[9] = encode32Chars[(id >> 15) & 31]; + buffer[8] = encode32Chars[(id >> 20) & 31]; + buffer[7] = encode32Chars[(id >> 25) & 31]; + buffer[6] = encode32Chars[(id >> 30) & 31]; + buffer[5] = encode32Chars[(id >> 35) & 31]; + buffer[4] = encode32Chars[(id >> 40) & 31]; + buffer[3] = encode32Chars[(id >> 45) & 31]; + buffer[2] = encode32Chars[(id >> 50) & 31]; + buffer[1] = encode32Chars[(id >> 55) & 31]; + buffer[0] = encode32Chars[(id >> 60) & 31]; + + // string `ctor` overload that takes `char*` + return new string(buffer, 0, 13); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Node/NodeJsEngine.cs b/src/JavaScriptEngineSwitcher.Node/NodeJsEngine.cs new file mode 100644 index 00000000..b3c0b8ea --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Node/NodeJsEngine.cs @@ -0,0 +1,560 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +using Jering.Javascript.NodeJS; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Constants; +using JavaScriptEngineSwitcher.Core.Helpers; +using JavaScriptEngineSwitcher.Core.Utilities; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; +using WrapperCompilationException = JavaScriptEngineSwitcher.Core.JsCompilationException; +using WrapperException = JavaScriptEngineSwitcher.Core.JsException; +using WrapperRuntimeException = JavaScriptEngineSwitcher.Core.JsRuntimeException; +using WrapperScriptException = JavaScriptEngineSwitcher.Core.JsScriptException; +using WrapperTimeoutException = JavaScriptEngineSwitcher.Core.JsTimeoutException; +using WrapperUsageException = JavaScriptEngineSwitcher.Core.JsUsageException; + +using JavaScriptEngineSwitcher.Node.Helpers; + +namespace JavaScriptEngineSwitcher.Node +{ + /// + /// Adapter for the Node JS engine + /// + public sealed class NodeJsEngine : JsEngineBase + { + /// + /// Name of resource, which contains a JS engine helpers + /// + private const string ENGINE_HELPERS_RESOURCE_NAME = "JavaScriptEngineSwitcher.Node.Resources.engine-helpers.js"; + + /// + /// Name of JS engine + /// + public const string EngineName = "NodeJsEngine"; + + /// + /// Node JS service + /// + private INodeJSService _jsService; + + /// + /// Version of original JS engine + /// + private string _engineVersion = "0.0.0"; + + /// + /// JS engine identifier + /// + private string _engineId; + + /// + /// Number of milliseconds to wait before the script execution times out + /// + private int _executionTimeout = -1; + + /// + /// Unique document name manager + /// + private UniqueDocumentNameManager _documentNameManager = new UniqueDocumentNameManager(DefaultDocumentName); + + /// + /// Regular expression for working with the timeout error message + /// + private static readonly Regex _timeoutErrorMessage = new Regex(@"^(?:Script execution|The Node invocation) " + + @"timed out after \d+ms"); + + /// + /// Regular expression for working with the error details + /// + private static readonly Regex _errorDetailsRegex = new Regex(@"^(?[^\r\n]+)(?:\r\n|\n|\r)" + + @"(?:" + + @"(?" + CommonRegExps.DocumentNamePattern + @")?:(?\d+)(?:\r\n|\n|\r)" + + @"(?[^\r\n]+)(?:\r\n|\n|\r)" + + @"(?[ \t]*\^)(?:\r\n|\n|\r){2}" + + @")?"); + + /// + /// Regular expression for working with the error message with type + /// + private static readonly Regex _errorMessageWithTypeRegex = + new Regex(@"^(?" + CommonRegExps.JsFullNamePattern + @"): (?[^\r\n]+)"); + + + /// + /// Constructs an instance of adapter for the Node JS engine + /// + public NodeJsEngine() + : this(DefaultNodeJsService.Instance, new NodeSettings()) + { } + + /// + /// Constructs an instance of adapter for the Node JS engine + /// + /// Node JS service + public NodeJsEngine(INodeJSService nodeJsService) + : this(nodeJsService, new NodeSettings()) + { } + + /// + /// Constructs an instance of adapter for the Node JS engine + /// + /// Settings of the Node JS engine + public NodeJsEngine(NodeSettings settings) + : this(DefaultNodeJsService.Instance, settings) + { } + + /// + /// Constructs an instance of adapter for the Node JS engine + /// + /// Node JS service + /// Settings of the Node JS engine + public NodeJsEngine(INodeJSService service, NodeSettings settings) + { + if (service == null) + { + throw new ArgumentNullException(nameof(service)); + } + + _jsService = service; + + try + { + Task versionTask = _jsService.InvokeFromStringAsync( + @"module.exports = (callback) => { + let version = process.versions.node; + callback(null , version); +};" + ); + _engineVersion = versionTask.ConfigureAwait(false).GetAwaiter().GetResult(); + } + catch (Exception e) + { + throw JsErrorHelpers.WrapEngineLoadException(e, EngineName, _engineVersion, true); + } + + NodeSettings nodeSettings = settings ?? new NodeSettings(); + _executionTimeout = (int)nodeSettings.TimeoutInterval.TotalMilliseconds; + _engineId = JsEngineIdGenerator.GetNextId(); + + InvokeEngineHelper("addContext", new object[] { _engineId, nodeSettings.UseBuiltinLibrary }); + } + + + private void InvokeEngineHelper(string exportName = null, object[] args = null) + { + Task cachedTask = _jsService.TryInvokeFromCacheAsync(ENGINE_HELPERS_RESOURCE_NAME, + exportName, args); + bool success = cachedTask.ConfigureAwait(false).GetAwaiter().GetResult(); + + if (!success) + { + Type type = typeof(NodeJsEngine); + Assembly assembly = type.Assembly; + + using (Stream resourceStream = assembly.GetManifestResourceStream(ENGINE_HELPERS_RESOURCE_NAME)) + { + Task task = _jsService.InvokeFromStreamAsync(resourceStream, ENGINE_HELPERS_RESOURCE_NAME, + exportName, args); + task.ConfigureAwait(false).GetAwaiter().GetResult(); + } + } + } + + private T InvokeEngineHelper(string exportName = null, object[] args = null) + { + Task<(bool, T)> cachedTask = _jsService.TryInvokeFromCacheAsync(ENGINE_HELPERS_RESOURCE_NAME, + exportName, args); + (bool success, T result) = cachedTask.ConfigureAwait(false).GetAwaiter().GetResult(); + + if (success) + { + return result; + } + else + { + Type type = typeof(NodeJsEngine); + Assembly assembly = type.Assembly; + + using (Stream resourceStream = assembly.GetManifestResourceStream(ENGINE_HELPERS_RESOURCE_NAME)) + { + Task task = _jsService.InvokeFromStreamAsync(resourceStream, ENGINE_HELPERS_RESOURCE_NAME, + exportName, args); + + return task.ConfigureAwait(false).GetAwaiter().GetResult(); + } + } + } + + #region Mapping + + /// + /// Makes a mapping of value from the host type to a script type + /// + /// The source value + /// The mapped value + private static object MapToScriptType(object value) + { + if (value is Undefined) + { + return null; + } + + return value; + } + + /// + /// Makes a mapping of array items from the host type to a script type + /// + /// The source array + /// The mapped array + public static object[] MapToScriptType(object[] args) + { + return args.Select(arg => MapToScriptType(arg)).ToArray(); + } + + #region Mapping + + private WrapperException WrapInvocationException(InvocationException originalException) + { + WrapperException wrapperException; + string message = originalException.Message; + string description = string.Empty; + + if (_timeoutErrorMessage.IsMatch(message)) + { + message = CoreStrings.Runtime_ScriptTimeoutExceeded; + description = message; + + var wrapperTimeoutException = new WrapperTimeoutException(message, EngineName, _engineVersion, + originalException) + { + Description = description + }; + + return wrapperTimeoutException; + } + + string documentName = string.Empty; + int lineNumber = 0; + int columnNumber = 0; + string sourceLine = string.Empty; + + Match detailsMatch = _errorDetailsRegex.Match(message); + int detailsLength = 0; + + if (detailsMatch.Success) + { + GroupCollection detailsGroups = detailsMatch.Groups; + description = detailsGroups["description"].Value; + documentName = detailsGroups["documentName"].Success ? + detailsGroups["documentName"].Value : string.Empty; + lineNumber = detailsGroups["lineNumber"].Success ? + int.Parse(detailsGroups["lineNumber"].Value) : 0; + columnNumber = NodeJsErrorHelpers.GetColumnCountFromLine(detailsGroups["pointer"].Value); + sourceLine = detailsGroups["sourceLine"].Value; + + detailsLength = detailsMatch.Length; + } + + message = detailsLength > 0 ? message.Substring(detailsLength) : message; + + Match messageWithTypeMatch = _errorMessageWithTypeRegex.Match(message); + if (messageWithTypeMatch.Success) + { + GroupCollection messageWithTypeGroups = messageWithTypeMatch.Groups; + string type = messageWithTypeGroups["type"].Value; + description = messageWithTypeGroups["description"].Value; + string sourceFragment = TextHelpers.GetTextFragmentFromLine(sourceLine, columnNumber); + + WrapperScriptException wrapperScriptException; + if (type == JsErrorType.Syntax) + { + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, + lineNumber, columnNumber, sourceFragment); + + wrapperScriptException = new WrapperCompilationException(message, EngineName, _engineVersion, + originalException); + } + else if (type == "UsageError") + { + wrapperException = new WrapperUsageException(description, EngineName, _engineVersion, + originalException); + wrapperException.Description = description; + + return wrapperException; + } + else + { + var errorLocationItems = new ErrorLocationItem[0]; + int messageLength = message.Length; + int messageWithTypeLength = messageWithTypeMatch.Length; + + if (messageWithTypeLength < messageLength) + { + string errorLocation = message.Substring(messageWithTypeLength); + errorLocationItems = NodeJsErrorHelpers.ParseErrorLocation(errorLocation); + errorLocationItems = NodeJsErrorHelpers.FilterErrorLocationItems(errorLocationItems); + + if (errorLocationItems.Length > 0) + { + ErrorLocationItem firstErrorLocationItem = errorLocationItems[0]; + documentName = firstErrorLocationItem.DocumentName; + lineNumber = firstErrorLocationItem.LineNumber; + columnNumber = firstErrorLocationItem.ColumnNumber; + + firstErrorLocationItem.SourceFragment = sourceFragment; + } + } + + string callStack = JsErrorHelpers.StringifyErrorLocationItems(errorLocationItems, true); + string callStackWithSourceFragment = JsErrorHelpers.StringifyErrorLocationItems( + errorLocationItems); + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, + callStackWithSourceFragment); + + wrapperScriptException = new WrapperRuntimeException(message, EngineName, _engineVersion, + originalException) + { + CallStack = callStack + }; + } + + wrapperScriptException.Type = type; + wrapperScriptException.DocumentName = documentName; + wrapperScriptException.LineNumber = lineNumber; + wrapperScriptException.ColumnNumber = columnNumber; + wrapperScriptException.SourceFragment = sourceFragment; + + wrapperException = wrapperScriptException; + } + else + { + wrapperException = new WrapperException(message, EngineName, _engineVersion, + originalException); + } + + wrapperException.Description = description; + + return wrapperException; + } + + #endregion + + #endregion + + #region JsEngineBase overrides + + protected override IPrecompiledScript InnerPrecompile(string code) + { + throw new NotSupportedException(); + } + + protected override IPrecompiledScript InnerPrecompile(string code, string documentName) + { + throw new NotSupportedException(); + } + + protected override object InnerEvaluate(string expression) + { + throw new NotSupportedException(); + } + + protected override object InnerEvaluate(string expression, string documentName) + { + throw new NotSupportedException(); + } + + protected override T InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override T InnerEvaluate(string expression, string documentName) + { + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + T result; + + try + { + result = InvokeEngineHelper("evaluate", new object[] { _engineId, expression, uniqueDocumentName, + _executionTimeout }); + } + catch (InvocationException e) + { + throw WrapInvocationException(e); + } + + return result; + } + + protected override void InnerExecute(string code) + { + InnerExecute(code, null); + } + + protected override void InnerExecute(string code, string documentName) + { + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + + try + { + InvokeEngineHelper("execute", new object[] { _engineId, code, uniqueDocumentName, _executionTimeout }); + } + catch (InvocationException e) + { + throw WrapInvocationException(e); + } + } + + protected override void InnerExecute(IPrecompiledScript precompiledScript) + { + throw new NotSupportedException(); + } + + protected override object InnerCallFunction(string functionName, params object[] args) + { + throw new NotSupportedException(); + } + + protected override T InnerCallFunction(string functionName, params object[] args) + { + T result; + object[] processedArgs = MapToScriptType(args); + + try + { + result = InvokeEngineHelper("callFunction", new object[] { _engineId, functionName, processedArgs, + _executionTimeout }); + } + catch (InvocationException e) + { + throw WrapInvocationException(e); + } + + return result; + } + + protected override bool InnerHasVariable(string variableName) + { + return InvokeEngineHelper("hasVariable", new [] { _engineId, variableName }); + } + + protected override object InnerGetVariableValue(string variableName) + { + throw new NotSupportedException(); + } + + protected override T InnerGetVariableValue(string variableName) + { + T result; + + try + { + result = InvokeEngineHelper("getVariableValue", new[] { _engineId, variableName }); + } + catch (InvocationException e) + { + throw WrapInvocationException(e); + } + + return result; + } + + protected override void InnerSetVariableValue(string variableName, object value) + { + object processedValue = MapToScriptType(value); + + try + { + InvokeEngineHelper("setVariableValue", new[] { _engineId, variableName, processedValue }); + } + catch (InvocationException e) + { + throw WrapInvocationException(e); + } + } + + protected override void InnerRemoveVariable(string variableName) + { + try + { + InvokeEngineHelper("removeVariable", new[] { _engineId, variableName }); + } + catch (InvocationException e) + { + throw WrapInvocationException(e); + } + } + + protected override void InnerEmbedHostObject(string itemName, object value) + { + throw new NotSupportedException(); + } + + protected override void InnerEmbedHostType(string itemName, Type type) + { + throw new NotSupportedException(); + } + + protected override void InnerInterrupt() + { + throw new NotSupportedException(); + } + + protected override void InnerCollectGarbage() + { + throw new NotSupportedException(); + } + + #region IJsEngine implementation + + public override string Name + { + get { return EngineName; } + } + + public override string Version + { + get { return _engineVersion; } + } + + public override bool SupportsScriptPrecompilation + { + get { return false; } + } + + public override bool SupportsScriptInterruption + { + get { return false; } + } + + public override bool SupportsGarbageCollection + { + get { return false; } + } + + #endregion + + #region IDisposable implementation + + public override void Dispose() + { + if (_disposedFlag.Set()) + { + InvokeEngineHelper("removeContext", new[] { _engineId }); + _jsService = null; + } + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Node/NodeJsEngineFactory.cs b/src/JavaScriptEngineSwitcher.Node/NodeJsEngineFactory.cs new file mode 100644 index 00000000..b805872a --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Node/NodeJsEngineFactory.cs @@ -0,0 +1,124 @@ +using Jering.Javascript.NodeJS; +using Microsoft.Extensions.DependencyInjection; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Node +{ + /// + /// Node JS engine factory + /// + public sealed class NodeJsEngineFactory : IJsEngineFactory + { + /// + /// The services available in the application + /// + private IServiceCollection _services; + + /// + /// Node JS service + /// + private INodeJSService _jsService; + + /// + /// Settings of the Node JS engine + /// + private readonly NodeSettings _settings; + + /// + /// Synchronizer of Node JS service creation + /// + private readonly object _creationSynchronizer = new object(); + + + /// + /// Constructs an instance of the Node JS engine factory + /// + public NodeJsEngineFactory() + : this(new NodeSettings()) + { } + + /// + /// Constructs an instance of the Node JS engine factory + /// + /// Node JS service + public NodeJsEngineFactory(INodeJSService service) + : this(service, new NodeSettings()) + { } + + /// + /// Constructs an instance of the Node JS engine factory + /// + /// The services available in the application + public NodeJsEngineFactory(IServiceCollection services) + : this(services, new NodeSettings()) + { } + + /// + /// Constructs an instance of the Node JS engine factory + /// + /// Settings of the Node JS engine + public NodeJsEngineFactory(NodeSettings settings) + { + _settings = settings; + } + + /// + /// Constructs an instance of the Node JS engine factory + /// + /// Node JS service + /// Settings of the Node JS engine + public NodeJsEngineFactory(INodeJSService service, NodeSettings settings) + { + _jsService = service; + _settings = settings; + } + + /// + /// Constructs an instance of the Node JS engine factory + /// + /// The services available in the application + /// Settings of the Node JS engine + public NodeJsEngineFactory(IServiceCollection services, NodeSettings settings) + { + _services = services; + _settings = settings; + } + + + #region IJsEngineFactory implementation + + /// + public string EngineName + { + get { return NodeJsEngine.EngineName; } + } + + + /// + /// Creates a instance of the Node JS engine + /// + /// Instance of the Node JS engine + public IJsEngine CreateEngine() + { + if (_services != null && _jsService == null) + { + lock (_creationSynchronizer) + { + if (_jsService == null) + { + ServiceProvider serviceProvider = _services.BuildServiceProvider(); + _jsService = serviceProvider.GetRequiredService(); + } + } + } + + IJsEngine engine = _jsService != null ? + new NodeJsEngine(_jsService, _settings) : new NodeJsEngine(_settings); + + return engine; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Node/NodeSettings.cs b/src/JavaScriptEngineSwitcher.Node/NodeSettings.cs new file mode 100644 index 00000000..271936fb --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Node/NodeSettings.cs @@ -0,0 +1,38 @@ +using System; + +namespace JavaScriptEngineSwitcher.Node +{ + /// + /// Settings of the Node JS engine + /// + public sealed class NodeSettings + { + /// + /// Gets or sets a interval to wait before the script execution times out + /// + public TimeSpan TimeoutInterval + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to use the Node.js built-in library + /// + public bool UseBuiltinLibrary + { + get; + set; + } + + + /// + /// Constructs an instance of the Node settings + /// + public NodeSettings() + { + TimeoutInterval = TimeSpan.Zero; + UseBuiltinLibrary = false; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Node/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.Node/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..76c940f9 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Node/PACKAGE-DESCRIPTION.md @@ -0,0 +1,4 @@ +JavaScriptEngineSwitcher.Node contains a `NodeJsEngine` adapter (wrapper for the [Jering.Javascript.NodeJS](https://github.com/JeringTech/Javascript.NodeJS) version 7.0.0). + +This package does not contain the `node.exe`. +Therefore, you need to install the [Node.js](https://nodejs.org) and add the `node.exe`'s directory to the `Path` environment variable (automatically done by the official installer). \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Node/Resources/engine-helpers.js b/src/JavaScriptEngineSwitcher.Node/Resources/engine-helpers.js new file mode 100644 index 00000000..d0c10831 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Node/Resources/engine-helpers.js @@ -0,0 +1,144 @@ +/*jshint esversion: 6*/ +/*globals global, module, require*/ +const GENERATED_FUNCTION_CALL_FILE_NAME = "JavaScriptEngineSwitcher.Node.Resources.generated-function-call.js"; +let vm = require('vm'); +let contexts = new Map(); + +class UsageError extends Error { + constructor(...args) { + super(...args); + this.name = 'UsageError'; + + Error.captureStackTrace(this, UsageError); + } +} + +function getContext(engineId) { + if (!contexts.has(engineId)) { + throw new UsageError("JavaScriptEngineSwitcher.Node module cannot work correctly " + + "when the Node JS service is configured to work in the multi-process mode of " + + "Jering.Javascript.NodeJS library (https://github.com/JeringTech/Javascript.NodeJS#enabling-multi-process-concurrency), " + + "because in this mode it is not possible to store a state of JS engine." + ); + } + + return contexts.get(engineId); +} + +module.exports = { + addContext: (callback, engineId, useBuiltinLibrary) => { + let sandbox; + if (useBuiltinLibrary) { + sandbox = Object.create(global); + if (typeof sandbox['require'] === 'undefined') { + sandbox['require'] = require; + } + } + else { + sandbox = Object.create(null); + } + + let context = vm.createContext(sandbox); + contexts.set(engineId, context); + + callback(null); + }, + + removeContext: (callback, engineId) => { + contexts.delete(engineId); + callback(null); + }, + + evaluate: (callback, engineId, expression, documentName, timeout) => { + let context = getContext(engineId); + let options = { filename: documentName }; + if (timeout > 0) { + options.timeout = timeout; + } + let result = vm.runInContext(expression, context, options); + + callback(null, result); + }, + + execute: (callback, engineId, code, documentName, timeout) => { + let context = getContext(engineId); + let options = { filename: documentName }; + if (timeout > 0) { + options.timeout = timeout; + } + + vm.runInContext(code, context, options); + + callback(null); + }, + + callFunction: (callback, engineId, functionName, args, timeout) => { + let context = getContext(engineId); + let result; + + if (timeout <= 0) { + let functionValue = context[functionName]; + result = functionValue.apply(null, args); + } + else { + let options = { + filename: GENERATED_FUNCTION_CALL_FILE_NAME, + timeout: timeout + }; + let argCount = args.length; + let expression; + + if (argCount > 0) { + expression = functionName; + expression += '('; + + for (let argIndex = 0; argIndex < argCount; argIndex++) { + if (argIndex > 0) { + expression += ', '; + } + + expression += JSON.stringify(args[argIndex]); + } + + expression += ');'; + } + else { + expression = `${functionName}();`; + } + + result = vm.runInContext(expression, context, options); + } + + callback(null, result); + }, + + hasVariable: (callback, engineId, variableName) => { + let context = getContext(engineId); + let result = typeof context[variableName] !== 'undefined'; + + callback(null, result); + }, + + getVariableValue: (callback, engineId, variableName) => { + let context = getContext(engineId); + let result = context[variableName]; + + callback(null, result); + }, + + setVariableValue: (callback, engineId, variableName, value) => { + let context = getContext(engineId); + context[variableName] = value; + + callback(null); + }, + + removeVariable: (callback, engineId, variableName) => { + let context = getContext(engineId); + if (typeof context[variableName] !== 'undefined') { + delete context[variableName]; + } + + callback(null); + } +}; \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Node/readme.txt b/src/JavaScriptEngineSwitcher.Node/readme.txt new file mode 100644 index 00000000..4f89b068 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Node/readme.txt @@ -0,0 +1,26 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: Node v3.24.1 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + JavaScriptEngineSwitcher.Node contains a `NodeJsEngine` adapter (wrapper for the + Jering.Javascript.NodeJS (https://github.com/JeringTech/Javascript.NodeJS) + version 7.0.0). + + This package does not contain the `node.exe`. Therefore, you need to install the + Node.js (https://nodejs.org) and add the `node.exe`'s directory to the `Path` + environment variable (automatically done by the official installer). + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/JavaScriptEngineSwitcher.V8.Native.linux-x64.csproj b/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/JavaScriptEngineSwitcher.V8.Native.linux-x64.csproj new file mode 100644 index 00000000..e761c1b1 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/JavaScriptEngineSwitcher.V8.Native.linux-x64.csproj @@ -0,0 +1,21 @@ + + + + JS Engine Switcher: V8 for Linux (x64) + 3.29.0 + netstandard2.0 + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png + This package is deprecated. Instead, it is recommended to use a Microsoft.ClearScript.V8.Native.linux-x64 package. + $(PackageCommonTags);V8;ClearScript;Linux;x64 + Microsoft ClearScript.V8 was updated to version 7.5 (support of the V8 version 13.3.415.23). + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/JavaScriptEngineSwitcher.V8.Native.linux-x64.nuspec b/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/JavaScriptEngineSwitcher.V8.Native.linux-x64.nuspec new file mode 100644 index 00000000..bf4bfdc0 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/JavaScriptEngineSwitcher.V8.Native.linux-x64.nuspec @@ -0,0 +1,14 @@ + + + + $CommonMetadataElements$ + + + + + + $CommonFileElements$ + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..3bb23eb4 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/PACKAGE-DESCRIPTION.md @@ -0,0 +1,2 @@ +This package is deprecated. +Instead, it is recommended to use a [Microsoft.ClearScript.V8.Native.linux-x64](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.linux-x64) package. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/readme.txt b/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/readme.txt new file mode 100644 index 00000000..9859dd74 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.linux-x64/readme.txt @@ -0,0 +1,27 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: V8 for Linux x64 v3.29.0 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2025 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + This package is deprecated. Instead, it is recommended to use a + Microsoft.ClearScript.V8.Native.linux-x64 package. + + ============= + RELEASE NOTES + ============= + Microsoft ClearScript.V8 was updated to version 7.5 (support of the V8 + version 13.3.415.23). + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/JavaScriptEngineSwitcher.V8.Native.osx-x64.csproj b/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/JavaScriptEngineSwitcher.V8.Native.osx-x64.csproj new file mode 100644 index 00000000..060da60d --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/JavaScriptEngineSwitcher.V8.Native.osx-x64.csproj @@ -0,0 +1,21 @@ + + + + JS Engine Switcher: V8 for OS X (x64) + 3.29.0 + netstandard2.0 + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png + This package is deprecated. Instead, it is recommended to use a Microsoft.ClearScript.V8.Native.osx-x64 package. + $(PackageCommonTags);V8;ClearScript;macOS;OSX;x64 + Microsoft ClearScript.V8 was updated to version 7.5 (support of the V8 version 13.3.415.23). + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/JavaScriptEngineSwitcher.V8.Native.osx-x64.nuspec b/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/JavaScriptEngineSwitcher.V8.Native.osx-x64.nuspec new file mode 100644 index 00000000..10bede4f --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/JavaScriptEngineSwitcher.V8.Native.osx-x64.nuspec @@ -0,0 +1,14 @@ + + + + $CommonMetadataElements$ + + + + + + $CommonFileElements$ + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..2455eca2 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/PACKAGE-DESCRIPTION.md @@ -0,0 +1,2 @@ +This package is deprecated. +Instead, it is recommended to use a [Microsoft.ClearScript.V8.Native.osx-x64](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.osx-x64) package. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/readme.txt b/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/readme.txt new file mode 100644 index 00000000..da4e5d5a --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.osx-x64/readme.txt @@ -0,0 +1,27 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: V8 for OS X x64 v3.29.0 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2025 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + This package is deprecated. Instead, it is recommended to use a + Microsoft.ClearScript.V8.Native.osx-x64 package. + + ============= + RELEASE NOTES + ============= + Microsoft ClearScript.V8 was updated to version 7.5 (support of the V8 + version 13.3.415.23). + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.win-x64/JavaScriptEngineSwitcher.V8.Native.win-x64.csproj b/src/JavaScriptEngineSwitcher.V8.Native.win-x64/JavaScriptEngineSwitcher.V8.Native.win-x64.csproj new file mode 100644 index 00000000..848e4af8 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.win-x64/JavaScriptEngineSwitcher.V8.Native.win-x64.csproj @@ -0,0 +1,21 @@ + + + + JS Engine Switcher: V8 for Windows (x64) + 3.29.0 + netstandard2.0 + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png + This package is deprecated. Instead, it is recommended to use a Microsoft.ClearScript.V8.Native.win-x64 package. + $(PackageCommonTags);V8;ClearScript;Windows;x64 + Microsoft ClearScript.V8 was updated to version 7.5 (support of the V8 version 13.3.415.23). + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.win-x64/JavaScriptEngineSwitcher.V8.Native.win-x64.nuspec b/src/JavaScriptEngineSwitcher.V8.Native.win-x64/JavaScriptEngineSwitcher.V8.Native.win-x64.nuspec new file mode 100644 index 00000000..7b52ae54 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.win-x64/JavaScriptEngineSwitcher.V8.Native.win-x64.nuspec @@ -0,0 +1,14 @@ + + + + $CommonMetadataElements$ + + + + + + $CommonFileElements$ + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.win-x64/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.V8.Native.win-x64/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..2efc6700 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.win-x64/PACKAGE-DESCRIPTION.md @@ -0,0 +1,2 @@ +This package is deprecated. +Instead, it is recommended to use a [Microsoft.ClearScript.V8.Native.win-x64](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.win-x64) package. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.win-x64/readme.txt b/src/JavaScriptEngineSwitcher.V8.Native.win-x64/readme.txt new file mode 100644 index 00000000..408c9a2c --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.win-x64/readme.txt @@ -0,0 +1,27 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: V8 for Windows x64 v3.29.0 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2025 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + This package is deprecated. Instead, it is recommended to use a + Microsoft.ClearScript.V8.Native.win-x64 package. + + ============= + RELEASE NOTES + ============= + Microsoft ClearScript.V8 was updated to version 7.5 (support of the V8 + version 13.3.415.23). + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.win-x86/JavaScriptEngineSwitcher.V8.Native.win-x86.csproj b/src/JavaScriptEngineSwitcher.V8.Native.win-x86/JavaScriptEngineSwitcher.V8.Native.win-x86.csproj new file mode 100644 index 00000000..d4261f95 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.win-x86/JavaScriptEngineSwitcher.V8.Native.win-x86.csproj @@ -0,0 +1,21 @@ + + + + JS Engine Switcher: V8 for Windows (x86) + 3.29.0 + netstandard2.0 + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png + This package is deprecated. Instead, it is recommended to use a Microsoft.ClearScript.V8.Native.win-x86 package. + $(PackageCommonTags);V8;ClearScript;Windows;x86 + Microsoft ClearScript.V8 was updated to version 7.5 (support of the V8 version 13.3.415.23). + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.win-x86/JavaScriptEngineSwitcher.V8.Native.win-x86.nuspec b/src/JavaScriptEngineSwitcher.V8.Native.win-x86/JavaScriptEngineSwitcher.V8.Native.win-x86.nuspec new file mode 100644 index 00000000..4c5670eb --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.win-x86/JavaScriptEngineSwitcher.V8.Native.win-x86.nuspec @@ -0,0 +1,14 @@ + + + + $CommonMetadataElements$ + + + + + + $CommonFileElements$ + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.win-x86/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.V8.Native.win-x86/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..44bc9064 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.win-x86/PACKAGE-DESCRIPTION.md @@ -0,0 +1,2 @@ +This package is deprecated. +Instead, it is recommended to use a [Microsoft.ClearScript.V8.Native.win-x86](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.win-x86) package. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8.Native.win-x86/readme.txt b/src/JavaScriptEngineSwitcher.V8.Native.win-x86/readme.txt new file mode 100644 index 00000000..d874dc4a --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8.Native.win-x86/readme.txt @@ -0,0 +1,27 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: V8 for Windows x86 v3.29.0 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2025 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + This package is deprecated. Instead, it is recommended to use a + Microsoft.ClearScript.V8.Native.win-x86 package. + + ============= + RELEASE NOTES + ============= + Microsoft ClearScript.V8 was updated to version 7.5 (support of the V8 + version 13.3.415.23). + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/AssemblyResolver.cs b/src/JavaScriptEngineSwitcher.V8/AssemblyResolver.cs deleted file mode 100644 index e2568d43..00000000 --- a/src/JavaScriptEngineSwitcher.V8/AssemblyResolver.cs +++ /dev/null @@ -1,92 +0,0 @@ -namespace JavaScriptEngineSwitcher.V8 -{ - using System; - using System.IO; - using System.Reflection; - using System.Text.RegularExpressions; - - using Resources; - - /// - /// Assembly resolver - /// - internal static class AssemblyResolver - { - /// - /// Name of directory, that contains the Microsoft ClearScript.V8 assemblies - /// - private const string ASSEMBLY_DIRECTORY_NAME = "ClearScript.V8"; - - /// - /// Name of the ClearScriptV8 assembly - /// - private const string ASSEMBLY_NAME = "ClearScriptV8"; - - /// - /// Regular expression for working with the `bin` directory path - /// - private static readonly Regex _binDirectoryRegex = new Regex(@"\\bin\\?$", RegexOptions.IgnoreCase); - - - /// - /// Initialize a assembly resolver - /// - public static void Initialize() - { - AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolveHandler; - } - - private static Assembly AssemblyResolveHandler(object sender, ResolveEventArgs args) - { - if (args.Name.StartsWith(ASSEMBLY_NAME, StringComparison.OrdinalIgnoreCase)) - { - var currentDomain = (AppDomain)sender; - string platform = Environment.Is64BitProcess ? "64" : "32"; - - string binDirectoryPath = currentDomain.SetupInformation.PrivateBinPath; - if (string.IsNullOrEmpty(binDirectoryPath)) - { - // `PrivateBinPath` property is empty in test scenarios, so - // need to use the `BaseDirectory` property - binDirectoryPath = currentDomain.BaseDirectory; - } - - string assemblyDirectoryPath = Path.Combine(binDirectoryPath, ASSEMBLY_DIRECTORY_NAME); - string assemblyFileName = string.Format("{0}-{1}.dll", ASSEMBLY_NAME, platform); - string assemblyFilePath = Path.Combine(assemblyDirectoryPath, assemblyFileName); - - if (!Directory.Exists(assemblyDirectoryPath)) - { - if (_binDirectoryRegex.IsMatch(binDirectoryPath)) - { - string applicationRootPath = _binDirectoryRegex.Replace(binDirectoryPath, string.Empty); - assemblyDirectoryPath = Path.Combine(applicationRootPath, ASSEMBLY_DIRECTORY_NAME); - - if (!Directory.Exists(assemblyDirectoryPath)) - { - throw new DirectoryNotFoundException( - string.Format(Strings.Engines_ClearScriptV8AssembliesDirectoryNotFound, assemblyDirectoryPath)); - } - - assemblyFilePath = Path.Combine(assemblyDirectoryPath, assemblyFileName); - } - else - { - throw new DirectoryNotFoundException( - string.Format(Strings.Engines_ClearScriptV8AssembliesDirectoryNotFound, assemblyDirectoryPath)); - } - } - - if (!File.Exists(assemblyFilePath)) - { - throw new FileNotFoundException( - string.Format(Strings.Engines_ClearScriptV8AssemblyFileNotFound, assemblyFilePath)); - } - - return Assembly.LoadFile(assemblyFilePath); - } - - return null; - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/Configuration/V8Configuration.cs b/src/JavaScriptEngineSwitcher.V8/Configuration/V8Configuration.cs deleted file mode 100644 index 4ddbe21c..00000000 --- a/src/JavaScriptEngineSwitcher.V8/Configuration/V8Configuration.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace JavaScriptEngineSwitcher.V8.Configuration -{ - using System.Configuration; - - /// - /// Configuration settings of V8 JavaScript engine - /// - public sealed class V8Configuration : ConfigurationSection - { - /// - /// Gets or sets a flag for whether to enable script debugging features - /// (allows a TCP/IP-based debugging) - /// - [ConfigurationProperty("enableDebugging", DefaultValue = false)] - public bool EnableDebugging - { - get { return (bool)this["enableDebugging"]; } - set { this["enableDebugging"] = value; } - } - - /// - /// Gets or sets a TCP/IP port on which to listen for a debugger connection - /// - [ConfigurationProperty("debugPort", DefaultValue = 9222)] - [IntegerValidator(MinValue = 0, MaxValue = 65535, ExcludeRange = false)] - public int DebugPort - { - get { return (int)this["debugPort"]; } - set { this["debugPort"] = value; } - } - - /// - /// Gets or sets a flag for whether to disable global members - /// - [ConfigurationProperty("disableGlobalMembers", DefaultValue = false)] - public bool DisableGlobalMembers - { - get { return (bool)this["disableGlobalMembers"]; } - set { this["disableGlobalMembers"] = value; } - } - - /// - /// Gets or sets a maximum size of the new object heap in mebibytes - /// - [ConfigurationProperty("maxNewSpaceSize", DefaultValue = 0)] - [IntegerValidator(MinValue = 0, MaxValue = int.MaxValue, ExcludeRange = false)] - public int MaxNewSpaceSize - { - get { return (int)this["maxNewSpaceSize"]; } - set { this["maxNewSpaceSize"] = value; } - } - - /// - /// Gets or sets a maximum size of the old object heap in mebibytes - /// - [ConfigurationProperty("maxOldSpaceSize", DefaultValue = 0)] - [IntegerValidator(MinValue = 0, MaxValue = int.MaxValue, ExcludeRange = false)] - public int MaxOldSpaceSize - { - get { return (int)this["maxOldSpaceSize"]; } - set { this["maxOldSpaceSize"] = value; } - } - - /// - /// Gets or sets a maximum size of the executable code heap in mebibytes - /// - [ConfigurationProperty("maxExecutableSize", DefaultValue = 0)] - [IntegerValidator(MinValue = 0, MaxValue = int.MaxValue, ExcludeRange = false)] - public int MaxExecutableSize - { - get { return (int)this["maxExecutableSize"]; } - set { this["maxExecutableSize"] = value; } - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/JavaScriptEngineSwitcher.V8.csproj b/src/JavaScriptEngineSwitcher.V8/JavaScriptEngineSwitcher.V8.csproj index a839ea1a..3ccf3374 100644 --- a/src/JavaScriptEngineSwitcher.V8/JavaScriptEngineSwitcher.V8.csproj +++ b/src/JavaScriptEngineSwitcher.V8/JavaScriptEngineSwitcher.V8.csproj @@ -1,129 +1,46 @@ - - - + + - Debug - AnyCPU - {1BAEC601-B244-48D3-BE27-351E133EEF73} + JS Engine Switcher: V8 + 3.29.1 + net462;net471;netstandard2.1;netcoreapp3.1;net5.0 Library - Properties - JavaScriptEngineSwitcher.V8 - JavaScriptEngineSwitcher.V8 - v4.0 - 512 - Client - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true + true + $(NoWarn);CS1591;NU1903 + false + true + true + false + + + + + - ..\..\JavaScriptEngineSwitcher.snk + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_V8_Logo128x128.png + JavaScriptEngineSwitcher.V8 contains a `V8JsEngine` adapter (wrapper for the Microsoft ClearScript.V8). + $(PackageCommonTags);V8;ClearScript + Performed a migration to a modern API for pre-compilation of scripts. + - - {5c903eef-bad1-43b8-bfe2-e4ee4d204410} - JavaScriptEngineSwitcher.Core - - - - - False - ..\..\Binaries\ClearScript\ClearScript.dll - - - - - - - - - - - - - True - True - Strings.resx - - - True - True - Strings.ru-ru.resx - - - - - - JavaScriptEngineSwitcher.snk - - - - - ResXFileCodeGenerator - Strings.Designer.cs - Designer - - - ResXFileCodeGenerator - Strings.ru-ru.Designer.cs - Designer - + + + + + + + - - ClearScript.V8\ClearScriptV8-32.dll - PreserveNewest - + + - - - ClearScript.V8\ClearScriptV8-64.dll - PreserveNewest - - - - - ClearScript.V8\v8-ia32.dll - PreserveNewest - - - - - ClearScript.V8\v8-x64.dll - PreserveNewest - - - - - - - - - - - - + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/JsEngineFactoryCollectionExtensions.cs b/src/JavaScriptEngineSwitcher.V8/JsEngineFactoryCollectionExtensions.cs new file mode 100644 index 00000000..0e2a95e3 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8/JsEngineFactoryCollectionExtensions.cs @@ -0,0 +1,79 @@ +using System; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.V8 +{ + /// + /// JS engine factory collection extensions + /// + public static class JsEngineFactoryCollectionExtensions + { + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Instance of + public static JsEngineFactoryCollection AddV8(this JsEngineFactoryCollection source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return source.AddV8(new V8Settings()); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The delegate to configure the provided + /// Instance of + public static JsEngineFactoryCollection AddV8(this JsEngineFactoryCollection source, + Action configure) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var settings = new V8Settings(); + configure(settings); + + return source.AddV8(settings); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Settings of the V8 JS engine + /// Instance of + public static JsEngineFactoryCollection AddV8(this JsEngineFactoryCollection source, + V8Settings settings) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + source.Add(new V8JsEngineFactory(settings)); + + return source; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/JsEngineSwitcherExtensions.cs b/src/JavaScriptEngineSwitcher.V8/JsEngineSwitcherExtensions.cs deleted file mode 100644 index e17c3a39..00000000 --- a/src/JavaScriptEngineSwitcher.V8/JsEngineSwitcherExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace JavaScriptEngineSwitcher.V8 -{ - using System; - using System.Configuration; - - using Core; - using Configuration; - - /// - /// JavaScript engine switcher extensions - /// - public static class JsEngineSwitcherExtensions - { - /// - /// Configuration settings of V8 JavaScript engine - /// - private static readonly Lazy _v8Config = - new Lazy(() => (V8Configuration)ConfigurationManager.GetSection("jsEngineSwitcher/v8")); - - /// - /// Gets a V8 JavaScript engine configuration settings - /// - /// JavaScript engine switcher> - /// Configuration settings of V8 JavaScript engine - public static V8Configuration GetV8Configuration(this JsEngineSwitcher switcher) - { - return _v8Config.Value; - } - } -} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.V8/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..5c20ea92 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8/PACKAGE-DESCRIPTION.md @@ -0,0 +1,14 @@ +JavaScriptEngineSwitcher.V8 contains a `V8JsEngine` adapter (wrapper for the [Microsoft ClearScript.V8](http://github.com/Microsoft/ClearScript) version 7.5). + +This package does not contain the native ClearScript.V8 assemblies. +Therefore, you need to choose and install the most appropriate package(s) for your platform. +The following packages are available: + + * [Microsoft.ClearScript.V8.Native.win-x86](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.win-x86) + * [Microsoft.ClearScript.V8.Native.win-x64](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.win-x64) + * [Microsoft.ClearScript.V8.Native.win-arm64](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.win-arm64) + * [Microsoft.ClearScript.V8.Native.linux-x64](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.linux-x64) + * [Microsoft.ClearScript.V8.Native.linux-arm](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.linux-arm) + * [Microsoft.ClearScript.V8.Native.linux-arm64](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.linux-arm64) + * [Microsoft.ClearScript.V8.Native.osx-x64](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.osx-x64) + * [Microsoft.ClearScript.V8.Native.osx-arm64](https://www.nuget.org/packages/Microsoft.ClearScript.V8.Native.osx-arm64) \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/Properties/AssemblyInfo.cs b/src/JavaScriptEngineSwitcher.V8/Properties/AssemblyInfo.cs deleted file mode 100644 index e708a18f..00000000 --- a/src/JavaScriptEngineSwitcher.V8/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("JavaScriptEngineSwitcher.V8")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JavaScript Engine Switcher for .Net: V8")] -[assembly: AssemblyCopyright("Copyright © 2013-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("684edd94-43fe-4486-a4f3-6b9197d10ebf")] - -[assembly: AssemblyVersion("1.5.8.0")] -[assembly: AssemblyFileVersion("1.5.8.0")] \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/Resources/Strings.Designer.cs b/src/JavaScriptEngineSwitcher.V8/Resources/Strings.Designer.cs index f54ec5c2..f4759a72 100644 --- a/src/JavaScriptEngineSwitcher.V8/Resources/Strings.Designer.cs +++ b/src/JavaScriptEngineSwitcher.V8/Resources/Strings.Designer.cs @@ -1,90 +1,75 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.34014 +// This code was generated by a tool. // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ +namespace JavaScriptEngineSwitcher.V8.Resources +{ + using System; + using System.Globalization; + using System.Reflection; + using System.Resources; + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + internal class Strings + { + private static Lazy _resourceManager = + new Lazy(() => new ResourceManager( + "JavaScriptEngineSwitcher.V8.Resources.Strings", +#if NET20 || NET30 || NET35 || NET40 + typeof(Strings).Assembly +#else + typeof(Strings).GetTypeInfo().Assembly +#endif + )); + + private static CultureInfo _resourceCulture; + + /// + /// Returns a cached ResourceManager instance used by this class + /// + internal static ResourceManager ResourceManager + { + get + { + return _resourceManager.Value; + } + } + + /// + /// Overrides a current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class + /// + internal static CultureInfo Culture + { + get + { + return _resourceCulture; + } + set + { + _resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to "Pre-compiled script is not accepted. To be accepted, the pre-compiled script must have been created..." + /// + internal static string Usage_PrecompiledScriptNotAccepted + { + get { return GetString("Usage_PrecompiledScriptNotAccepted"); } + } + + private static string GetString(string name) + { + string value = ResourceManager.GetString(name, _resourceCulture); -namespace JavaScriptEngineSwitcher.V8.Resources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Strings { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Strings() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JavaScriptEngineSwitcher.V8.Resources.Strings", typeof(Strings).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Failed to load the ClearScript `undefined` value.. - /// - internal static string Engines_ClearScriptUndefinedValueNotLoaded { - get { - return ResourceManager.GetString("Engines_ClearScriptUndefinedValueNotLoaded", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to load the ClearScriptV8 assembly, because the directory '{0}' does not exist.. - /// - internal static string Engines_ClearScriptV8AssembliesDirectoryNotFound { - get { - return ResourceManager.GetString("Engines_ClearScriptV8AssembliesDirectoryNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to load the ClearScriptV8 assembly, because the file '{0}' does not exist.. - /// - internal static string Engines_ClearScriptV8AssemblyFileNotFound { - get { - return ResourceManager.GetString("Engines_ClearScriptV8AssemblyFileNotFound", resourceCulture); - } - } - } -} + return value; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/Resources/Strings.resx b/src/JavaScriptEngineSwitcher.V8/Resources/Strings.resx index 69410b01..91d19a75 100644 --- a/src/JavaScriptEngineSwitcher.V8/Resources/Strings.resx +++ b/src/JavaScriptEngineSwitcher.V8/Resources/Strings.resx @@ -117,13 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Failed to load the ClearScript `undefined` value. - - - Failed to load the ClearScriptV8 assembly, because the directory '{0}' does not exist. - - - Failed to load the ClearScriptV8 assembly, because the file '{0}' does not exist. + + Pre-compiled script is not accepted. To be accepted, the pre-compiled script must have been created by the same V8 build. \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/Resources/Strings.ru-ru.Designer.cs b/src/JavaScriptEngineSwitcher.V8/Resources/Strings.ru-RU.Designer.cs similarity index 100% rename from src/JavaScriptEngineSwitcher.V8/Resources/Strings.ru-ru.Designer.cs rename to src/JavaScriptEngineSwitcher.V8/Resources/Strings.ru-RU.Designer.cs diff --git a/src/JavaScriptEngineSwitcher.V8/Resources/Strings.ru-RU.resx b/src/JavaScriptEngineSwitcher.V8/Resources/Strings.ru-RU.resx new file mode 100644 index 00000000..816526ea --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8/Resources/Strings.ru-RU.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Предварительно скомпилированный сценарий не принят! Чтобы быть принятым, предварительно скомпилированный сценарий должен быть создан той же сборкой V8. + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/V8JsEngine.cs b/src/JavaScriptEngineSwitcher.V8/V8JsEngine.cs index ae27081f..6b5f74db 100644 --- a/src/JavaScriptEngineSwitcher.V8/V8JsEngine.cs +++ b/src/JavaScriptEngineSwitcher.V8/V8JsEngine.cs @@ -1,209 +1,183 @@ -namespace JavaScriptEngineSwitcher.V8 +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +using OriginalCacheKind = Microsoft.ClearScript.V8.V8CacheKind; +using OriginalCacheResult = Microsoft.ClearScript.V8.V8CacheResult; +using OriginalDocumentFlags = Microsoft.ClearScript.DocumentFlags; +using OriginalDocumentInfo = Microsoft.ClearScript.DocumentInfo; +using OriginalEngine = Microsoft.ClearScript.V8.V8ScriptEngine; +using OriginalEngineFlags = Microsoft.ClearScript.V8.V8ScriptEngineFlags; +using OriginalException = Microsoft.ClearScript.ScriptEngineException; +using OriginalInterruptedException = Microsoft.ClearScript.ScriptInterruptedException; +using OriginalRuntimeConstraints = Microsoft.ClearScript.V8.V8RuntimeConstraints; +using OriginalScript = Microsoft.ClearScript.V8.V8Script; +using OriginalUndefined = Microsoft.ClearScript.Undefined; + +using AdvancedStringBuilder; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Constants; +using JavaScriptEngineSwitcher.Core.Extensions; +using JavaScriptEngineSwitcher.Core.Helpers; +using JavaScriptEngineSwitcher.Core.Utilities; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; +using WrapperCompilationException = JavaScriptEngineSwitcher.Core.JsCompilationException; +using WrapperEngineLoadException = JavaScriptEngineSwitcher.Core.JsEngineLoadException; +using WrapperException = JavaScriptEngineSwitcher.Core.JsException; +using WrapperFatalException = JavaScriptEngineSwitcher.Core.JsFatalException; +using WrapperInterruptedException = JavaScriptEngineSwitcher.Core.JsInterruptedException; +using WrapperRuntimeException = JavaScriptEngineSwitcher.Core.JsRuntimeException; +using WrapperScriptException = JavaScriptEngineSwitcher.Core.JsScriptException; +using WrapperUsageException = JavaScriptEngineSwitcher.Core.JsUsageException; + +using JavaScriptEngineSwitcher.V8.Resources; + +namespace JavaScriptEngineSwitcher.V8 { - using System; - using System.Reflection; - using System.Text.RegularExpressions; - - using Microsoft.ClearScript.V8; - using OriginalJsException = Microsoft.ClearScript.ScriptEngineException; - using OriginalUndefined = Microsoft.ClearScript.Undefined; - - using Core; - using Core.Utilities; - using CoreStrings = Core.Resources.Strings; - - using Configuration; - using Resources; - /// - /// Adapter for Microsoft ClearScript.V8 + /// Adapter for the V8 JS engine (Microsoft ClearScript.V8) /// public sealed class V8JsEngine : JsEngineBase { /// - /// Name of JavaScript engine + /// Name of JS engine /// - private const string ENGINE_NAME = "V8 JavaScript engine"; + public const string EngineName = "V8JsEngine"; /// - /// Version of original JavaScript engine + /// Version of original JS engine /// - private const string ENGINE_VERSION = "5.1.281.65"; + private const string EngineVersion = "13.3.415.23"; /// - /// JS-engine + /// V8 JS engine /// - private V8ScriptEngine _jsEngine; + private OriginalEngine _jsEngine; /// - /// ClearScript undefined value + /// Regular expression for working with the error message with type /// - private static OriginalUndefined _originalUndefinedValue; + private static readonly Regex _errorMessageWithTypeRegex = + new Regex(@"^(?" + CommonRegExps.JsFullNamePattern + @"):\s+(?[\s\S]+?)$"); /// - /// Information about `InvokeMethod` method of `Microsoft.ClearScript.Windows.WindowsScriptItem` type + /// Regular expression for working with the library load error message /// - private static MethodInfo _winScriptItemInvokeMethodInfo; + private static readonly Regex _libraryLoadErrorMessage = + new Regex(@"^Cannot load ClearScript V8 library. " + + "Load failure information for (?" + CommonRegExps.DocumentNamePattern + "):"); /// - /// Regular expression for working with the string representation of error + /// Mapping of native assemblies and NuGet packages /// - private static readonly Regex _errorStringRegex = - new Regex(@"at (?:[A-Za-z_\$][0-9A-Za-z_\$]* )?" + - @"\(?Script Document(?:\s*\[\d+\])?:(?\d+):(?\d+)\)?"); - - /// - /// Synchronizer of code execution - /// - private readonly object _executionSynchronizer = new object(); - - /// - /// Gets a name of JavaScript engine - /// - public override string Name + private static readonly Dictionary _nativeAssemblyPackageMap = new Dictionary { - get { return ENGINE_NAME; } - } + { "ClearScriptV8.win-x86.dll", "Microsoft.ClearScript.V8.Native.win-x86" }, + { "ClearScriptV8.win-x64.dll", "Microsoft.ClearScript.V8.Native.win-x64" }, + { "ClearScriptV8.win-arm64.dll", "Microsoft.ClearScript.V8.Native.win-arm64" }, + { "ClearScriptV8.linux-x64.so", "Microsoft.ClearScript.V8.Native.linux-x64" }, + { "ClearScriptV8.linux-arm.so", "Microsoft.ClearScript.V8.Native.linux-arm" }, + { "ClearScriptV8.linux-arm64.so", "Microsoft.ClearScript.V8.Native.linux-arm64" }, + { "ClearScriptV8.osx-x64.dylib", "Microsoft.ClearScript.V8.Native.osx-x64" }, + { "ClearScriptV8.osx-arm64.dylib", "Microsoft.ClearScript.V8.Native.osx-arm64" } + }; - /// - /// Gets a version of original JavaScript engine - /// - public override string Version - { - get { return ENGINE_VERSION; } - } - - - /// - /// Static constructor - /// - static V8JsEngine() - { - AssemblyResolver.Initialize(); - LoadUndefinedValue(); - LoadWinScriptItemInvokeMethodInfo(); - } /// - /// Constructs a instance of adapter for Microsoft ClearScript.V8 + /// Constructs an instance of adapter for the V8 JS engine (Microsoft ClearScript.V8) /// public V8JsEngine() - : this(JsEngineSwitcher.Current.GetV8Configuration()) + : this(new V8Settings()) { } /// - /// Constructs a instance of adapter for Microsoft ClearScript.V8 + /// Constructs an instance of adapter for the V8 JS engine (Microsoft ClearScript.V8) /// - /// Configuration settings of V8 JavaScript engine - public V8JsEngine(V8Configuration config) + /// Settings of the V8 JS engine + public V8JsEngine(V8Settings settings) { - V8Configuration v8Config = config ?? new V8Configuration(); + V8Settings v8Settings = settings ?? new V8Settings(); - V8RuntimeConstraints constraints = new V8RuntimeConstraints + var constraints = new OriginalRuntimeConstraints { - MaxNewSpaceSize = v8Config.MaxNewSpaceSize, - MaxOldSpaceSize = v8Config.MaxOldSpaceSize, - MaxExecutableSize = v8Config.MaxExecutableSize + HeapExpansionMultiplier = v8Settings.HeapExpansionMultiplier, + MaxArrayBufferAllocation = v8Settings.MaxArrayBufferAllocation, + MaxNewSpaceSize = v8Settings.MaxNewSpaceSize, + MaxOldSpaceSize = v8Settings.MaxOldSpaceSize }; - V8ScriptEngineFlags flags = V8ScriptEngineFlags.None; - if (v8Config.EnableDebugging && v8Config.DisableGlobalMembers) + OriginalEngineFlags flags = OriginalEngineFlags.None; + if (v8Settings.AddPerformanceObject) { - flags = V8ScriptEngineFlags.EnableDebugging | V8ScriptEngineFlags.DisableGlobalMembers; + flags |= OriginalEngineFlags.AddPerformanceObject; } - else if (v8Config.EnableDebugging || v8Config.DisableGlobalMembers) + if (v8Settings.AwaitDebuggerAndPauseOnStart) { - flags = v8Config.EnableDebugging ? - V8ScriptEngineFlags.EnableDebugging : V8ScriptEngineFlags.DisableGlobalMembers; + flags |= OriginalEngineFlags.AwaitDebuggerAndPauseOnStart; } - - int debugPort = v8Config.DebugPort; - - try + if (v8Settings.EnableDebugging) { - _jsEngine = new V8ScriptEngine(constraints, flags, debugPort); + flags |= OriginalEngineFlags.EnableDebugging; } - catch (Exception e) + if (v8Settings.EnableRemoteDebugging) { - throw new JsEngineLoadException( - string.Format(CoreStrings.Runtime_JsEngineNotLoaded, - ENGINE_NAME, e.Message), ENGINE_NAME, ENGINE_VERSION, e); + flags |= OriginalEngineFlags.EnableRemoteDebugging; } - } - - - /// - /// Loads a ClearScript undefined value - /// - private static void LoadUndefinedValue() - { - FieldInfo undefinedValueFieldInfo = typeof(OriginalUndefined).GetField("Value", - BindingFlags.NonPublic | BindingFlags.Static); - OriginalUndefined originalUndefinedValue = null; - - if (undefinedValueFieldInfo != null) - { - originalUndefinedValue = undefinedValueFieldInfo.GetValue(null) as OriginalUndefined; - } - - if (originalUndefinedValue != null) + if (v8Settings.DisableGlobalMembers) { - _originalUndefinedValue = originalUndefinedValue; + flags |= OriginalEngineFlags.DisableGlobalMembers; } - else + if (v8Settings.SetTimerResolution) { - throw new JsEngineLoadException(Strings.Engines_ClearScriptUndefinedValueNotLoaded, - ENGINE_NAME, ENGINE_VERSION); + flags |= OriginalEngineFlags.SetTimerResolution; } - } - - /// - /// Loads a `InvokeMethod` method information of `Microsoft.ClearScript.Windows.WindowsScriptItem` type - /// - private static void LoadWinScriptItemInvokeMethodInfo() - { - const string typeName = "Microsoft.ClearScript.V8.V8ScriptItem"; - const string methodName = "InvokeMethod"; - Assembly clearScriptAssembly = typeof(V8ScriptEngine).Assembly; - Type winScriptItemType = clearScriptAssembly.GetType(typeName); - MethodInfo winScriptItemInvokeMethodInfo = null; + int debugPort = v8Settings.DebugPort; - if (winScriptItemType != null) + try { - winScriptItemInvokeMethodInfo = winScriptItemType.GetMethod(methodName, - BindingFlags.Instance | BindingFlags.Public); + _jsEngine = new OriginalEngine(constraints, flags, debugPort) + { + AllowReflection = v8Settings.AllowReflection, + DisableDynamicBinding = v8Settings.DisableDynamicBinding, + MaxRuntimeHeapSize = v8Settings.MaxHeapSize, + RuntimeHeapSizeSampleInterval = v8Settings.HeapSizeSampleInterval, + MaxRuntimeStackUsage = v8Settings.MaxStackUsage + }; } - - if (winScriptItemInvokeMethodInfo != null) + catch (TypeLoadException e) { - _winScriptItemInvokeMethodInfo = winScriptItemInvokeMethodInfo; + throw WrapTypeLoadException(e); } - else + catch (Exception e) { - throw new JsEngineLoadException( - string.Format(CoreStrings.Runtime_MethodInfoNotLoaded, typeName, methodName), - ENGINE_NAME, ENGINE_VERSION); + throw JsErrorHelpers.WrapEngineLoadException(e, EngineName, EngineVersion, true); } } + + #region Mapping + /// - /// Executes a mapping from the host type to a ClearScript type + /// Makes a mapping of value from the host type to a script type /// /// The source value /// The mapped value - private static object MapToClearScriptType(object value) + private static object MapToScriptType(object value) { if (value is Undefined) { - return _originalUndefinedValue; + return OriginalUndefined.Value; } return value; } /// - /// Executes a mapping from the ClearScript type to a host type + /// Makes a mapping of value from the script type to a host type /// /// The source value /// The mapped value @@ -217,50 +191,224 @@ private static object MapToHostType(object value) return value; } - private JsRuntimeException ConvertScriptEngineExceptionToJsRuntimeException( - OriginalJsException scriptEngineException) + private static WrapperException WrapScriptEngineException(OriginalException originalException) { - string errorDetails = scriptEngineException.ErrorDetails; + WrapperException wrapperException; + string message = originalException.Message; + string messageWithErrorLocation = originalException.ErrorDetails; + string description = message; + string type = string.Empty; + string documentName = string.Empty; int lineNumber = 0; int columnNumber = 0; + string callStack = string.Empty; + string sourceFragment = string.Empty; - Match errorStringMatch = _errorStringRegex.Match(errorDetails); - if (errorStringMatch.Success) + if (originalException.IsFatal) + { + if (message == "The V8 runtime has exceeded its memory limit") + { + wrapperException = new WrapperRuntimeException(message, EngineName, EngineVersion, + originalException); + } + else + { + wrapperException = new WrapperFatalException(message, EngineName, EngineVersion, + originalException); + } + } + else { - GroupCollection errorStringGroups = errorStringMatch.Groups; + Match messageWithTypeMatch = _errorMessageWithTypeRegex.Match(message); + if (messageWithTypeMatch.Success) + { + GroupCollection messageWithTypeGroups = messageWithTypeMatch.Groups; + type = messageWithTypeGroups["type"].Value; + description = messageWithTypeGroups["description"].Value; + var errorLocationItems = new ErrorLocationItem[0]; + + if (message.Length < messageWithErrorLocation.Length) + { + string errorLocation = messageWithErrorLocation + .TrimStart(message) + .TrimStart(new char[] { '\n', '\r' }) + ; + + errorLocationItems = JsErrorHelpers.ParseErrorLocation(errorLocation); + if (errorLocationItems.Length > 0) + { + ErrorLocationItem firstErrorLocationItem = errorLocationItems[0]; + + documentName = firstErrorLocationItem.DocumentName; + lineNumber = firstErrorLocationItem.LineNumber; + columnNumber = firstErrorLocationItem.ColumnNumber; + string sourceLine = firstErrorLocationItem.SourceFragment; + sourceFragment = TextHelpers.GetTextFragmentFromLine(sourceLine, columnNumber); - lineNumber = int.Parse(errorStringGroups["lineNumber"].Value); - columnNumber = int.Parse(errorStringGroups["columnNumber"].Value); + firstErrorLocationItem.SourceFragment = sourceFragment; + } + } + + WrapperScriptException wrapperScriptException; + if (type == JsErrorType.Syntax) + { + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, + lineNumber, columnNumber, sourceFragment); + + wrapperScriptException = new WrapperCompilationException(message, EngineName, EngineVersion, + originalException); + } + else + { + callStack = JsErrorHelpers.StringifyErrorLocationItems(errorLocationItems, true); + string callStackWithSourceFragment = JsErrorHelpers.StringifyErrorLocationItems( + errorLocationItems); + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, + callStackWithSourceFragment); + + wrapperScriptException = new WrapperRuntimeException(message, EngineName, EngineVersion, + originalException) + { + CallStack = callStack + }; + } + + wrapperScriptException.Type = type; + wrapperScriptException.DocumentName = documentName; + wrapperScriptException.LineNumber = lineNumber; + wrapperScriptException.ColumnNumber = columnNumber; + wrapperScriptException.SourceFragment = sourceFragment; + + wrapperException = wrapperScriptException; + } + else + { + wrapperException = new WrapperException(message, EngineName, EngineVersion, + originalException); + } } - var jsRuntimeException = new JsRuntimeException(errorDetails, ENGINE_NAME, ENGINE_VERSION, - scriptEngineException) - { - LineNumber = lineNumber, - ColumnNumber = columnNumber, - Source = scriptEngineException.Source, - HelpLink = scriptEngineException.HelpLink - }; + wrapperException.Description = description; - return jsRuntimeException; + return wrapperException; } - #region JsEngineBase implementation + private static WrapperInterruptedException WrapScriptInterruptedException( + OriginalInterruptedException originalInterruptedException) + { + string message = CoreStrings.Runtime_ScriptInterrupted; + string description = message; - protected override object InnerEvaluate(string expression) + var wrapperInterruptedException = new WrapperInterruptedException(message, EngineName, EngineVersion, + originalInterruptedException) + { + Description = description + } + ; + + return wrapperInterruptedException; + } + + private static WrapperEngineLoadException WrapTypeLoadException( + TypeLoadException originalTypeLoadException) { - object result; + string originalMessage = originalTypeLoadException.Message; + string description; + string message; - lock (_executionSynchronizer) + Match errorMessageMatch = _libraryLoadErrorMessage.Match(originalMessage); + if (errorMessageMatch.Success) { - try + string assemblyFileName = errorMessageMatch.Groups["assemblyFileName"].Value; + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder descriptionBuilder = stringBuilderPool.Rent(); + descriptionBuilder.AppendFormat(CoreStrings.Engine_AssemblyNotFound, assemblyFileName); + descriptionBuilder.Append(" "); + + string packageName; + if (_nativeAssemblyPackageMap.TryGetValue(assemblyFileName, out packageName)) { - result = _jsEngine.Evaluate(expression); + descriptionBuilder.AppendFormat(CoreStrings.Engine_NuGetPackageInstallationRequired, packageName); } - catch (OriginalJsException e) + else { - throw ConvertScriptEngineExceptionToJsRuntimeException(e); + descriptionBuilder.AppendFormat(CoreStrings.Common_SeeOriginalErrorMessage, originalMessage); } + + description = descriptionBuilder.ToString(); + stringBuilderPool.Return(descriptionBuilder); + + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, EngineName); + } + else + { + description = originalMessage; + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, EngineName, true); + } + + var wrapperEngineLoadException = new WrapperEngineLoadException(message, EngineName, EngineVersion, + originalTypeLoadException) + { + Description = description + }; + + return wrapperEngineLoadException; + } + + #endregion + + #region JsEngineBase overrides + + protected override IPrecompiledScript InnerPrecompile(string code) + { + return Precompile(code, null); + } + + protected override IPrecompiledScript InnerPrecompile(string code, string documentName) + { + var documentInfo = new OriginalDocumentInfo(documentName); + var cacheKind = OriginalCacheKind.Code; + byte[] cachedBytes; + + try + { + using (OriginalScript generalScript = _jsEngine.Compile(documentInfo, code, cacheKind, + out cachedBytes)) + { } + } + catch (OriginalException e) + { + throw WrapScriptEngineException(e); + } + + return new V8PrecompiledScript(code, cacheKind, cachedBytes, documentInfo); + } + + protected override object InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override object InnerEvaluate(string expression, string documentName) + { + var documentInfo = new OriginalDocumentInfo(documentName) + { + Flags = OriginalDocumentFlags.None + }; + object result; + + try + { + result = _jsEngine.Evaluate(documentInfo, expression); + } + catch (OriginalException e) + { + throw WrapScriptEngineException(e); + } + catch (OriginalInterruptedException e) + { + throw WrapScriptInterruptedException(e); } result = MapToHostType(result); @@ -270,24 +418,78 @@ protected override object InnerEvaluate(string expression) protected override T InnerEvaluate(string expression) { - object result = InnerEvaluate(expression); + return InnerEvaluate(expression, null); + } + + protected override T InnerEvaluate(string expression, string documentName) + { + object result = InnerEvaluate(expression, documentName); return TypeConverter.ConvertToType(result); } protected override void InnerExecute(string code) { - lock (_executionSynchronizer) + InnerExecute(code, null); + } + + protected override void InnerExecute(string code, string documentName) + { + var documentInfo = new OriginalDocumentInfo(documentName) { - try - { - _jsEngine.Execute(code); - } - catch (OriginalJsException e) + Flags = OriginalDocumentFlags.None + }; + + try + { + _jsEngine.Execute(documentInfo, code); + } + catch (OriginalException e) + { + throw WrapScriptEngineException(e); + } + catch (OriginalInterruptedException e) + { + throw WrapScriptInterruptedException(e); + } + } + + protected override void InnerExecute(IPrecompiledScript precompiledScript) + { + var v8PrecompiledScript = precompiledScript as V8PrecompiledScript; + if (v8PrecompiledScript == null) + { + throw new WrapperUsageException( + string.Format(CoreStrings.Usage_CannotConvertPrecompiledScriptToInternalType, + typeof(V8PrecompiledScript).FullName), + Name, Version + ); + } + + byte[] cachedBytes = v8PrecompiledScript.CachedBytes; + + try + { + using (OriginalScript script = _jsEngine.Compile(v8PrecompiledScript.DocumentInfo, + v8PrecompiledScript.Code, v8PrecompiledScript.CacheKind, ref cachedBytes, + out var cacheResult)) { - throw ConvertScriptEngineExceptionToJsRuntimeException(e); + if (cacheResult != OriginalCacheResult.Accepted && cacheResult != OriginalCacheResult.Verified) + { + throw new WrapperUsageException(Strings.Usage_PrecompiledScriptNotAccepted, Name, Version); + } + + _jsEngine.Execute(script); } } + catch (OriginalException e) + { + throw WrapScriptEngineException(e); + } + catch (OriginalInterruptedException e) + { + throw WrapScriptInterruptedException(e); + } } protected override object InnerCallFunction(string functionName, params object[] args) @@ -300,33 +502,21 @@ protected override object InnerCallFunction(string functionName, params object[] { for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) { - processedArgs[argumentIndex] = MapToClearScriptType(args[argumentIndex]); + processedArgs[argumentIndex] = MapToScriptType(args[argumentIndex]); } } - lock (_executionSynchronizer) + try { - try - { - object obj = _jsEngine.Script; - result = _winScriptItemInvokeMethodInfo.Invoke(obj, new object[] {functionName, processedArgs}); - } - catch (TargetInvocationException e) - { - Exception innerException = e.InnerException; - if (innerException != null) - { - var scriptEngineException = e.InnerException as OriginalJsException; - if (scriptEngineException != null) - { - throw ConvertScriptEngineExceptionToJsRuntimeException(scriptEngineException); - } - - throw innerException; - } - - throw; - } + result = _jsEngine.Invoke(functionName, processedArgs); + } + catch (OriginalException e) + { + throw WrapScriptEngineException(e); + } + catch (OriginalInterruptedException e) + { + throw WrapScriptInterruptedException(e); } result = MapToHostType(result); @@ -343,8 +533,21 @@ protected override T InnerCallFunction(string functionName, params object[] a protected override bool InnerHasVariable(string variableName) { - string expression = string.Format("(typeof {0} !== 'undefined');", variableName); - var result = InnerEvaluate(expression); + bool result; + + try + { + object variableValue = _jsEngine.Global.GetProperty(variableName); + result = variableValue != OriginalUndefined.Value; + } + catch (OriginalException e) + { + throw WrapScriptEngineException(e); + } + catch (OriginalInterruptedException e) + { + throw WrapScriptInterruptedException(e); + } return result; } @@ -353,16 +556,17 @@ protected override object InnerGetVariableValue(string variableName) { object result; - lock (_executionSynchronizer) + try { - try - { - result = _jsEngine.Script[variableName]; - } - catch (OriginalJsException e) - { - throw ConvertScriptEngineExceptionToJsRuntimeException(e); - } + result = _jsEngine.Global.GetProperty(variableName); + } + catch (OriginalException e) + { + throw WrapScriptEngineException(e); + } + catch (OriginalInterruptedException e) + { + throw WrapScriptInterruptedException(e); } result = MapToHostType(result); @@ -379,56 +583,107 @@ protected override T InnerGetVariableValue(string variableName) protected override void InnerSetVariableValue(string variableName, object value) { - object processedValue = MapToClearScriptType(value); + object processedValue = MapToScriptType(value); - lock (_executionSynchronizer) + try { - try - { - _jsEngine.Script[variableName] = processedValue; - } - catch (OriginalJsException e) - { - throw ConvertScriptEngineExceptionToJsRuntimeException(e); - } + _jsEngine.Global.SetProperty(variableName, processedValue); + } + catch (OriginalException e) + { + throw WrapScriptEngineException(e); + } + catch (OriginalInterruptedException e) + { + throw WrapScriptInterruptedException(e); } } protected override void InnerRemoveVariable(string variableName) { - InnerSetVariableValue(variableName, Undefined.Value); + try + { + _jsEngine.Global.DeleteProperty(variableName); + } + catch (OriginalException e) + { + throw WrapScriptEngineException(e); + } + catch (OriginalInterruptedException e) + { + throw WrapScriptInterruptedException(e); + } } protected override void InnerEmbedHostObject(string itemName, object value) { - object processedValue = MapToClearScriptType(value); + object processedValue = MapToScriptType(value); - lock (_executionSynchronizer) + try { - try - { - _jsEngine.AddHostObject(itemName, processedValue); - } - catch (OriginalJsException e) - { - throw ConvertScriptEngineExceptionToJsRuntimeException(e); - } + _jsEngine.AddHostObject(itemName, processedValue); + } + catch (OriginalException e) + { + throw WrapScriptEngineException(e); + } + catch (OriginalInterruptedException e) + { + throw WrapScriptInterruptedException(e); } } protected override void InnerEmbedHostType(string itemName, Type type) { - lock (_executionSynchronizer) + try { - try - { - _jsEngine.AddHostType(itemName, type); - } - catch (OriginalJsException e) - { - throw ConvertScriptEngineExceptionToJsRuntimeException(e); - } + _jsEngine.AddHostType(itemName, type); + } + catch (OriginalException e) + { + throw WrapScriptEngineException(e); } + catch (OriginalInterruptedException e) + { + throw WrapScriptInterruptedException(e); + } + } + + protected override void InnerInterrupt() + { + _jsEngine.Interrupt(); + } + + protected override void InnerCollectGarbage() + { + _jsEngine.CollectGarbage(true); + } + + #region IJsEngine implementation + + public override string Name + { + get { return EngineName; } + } + + public override string Version + { + get { return EngineVersion; } + } + + public override bool SupportsScriptPrecompilation + { + get { return true; } + } + + public override bool SupportsScriptInterruption + { + get { return true; } + } + + public override bool SupportsGarbageCollection + { + get { return true; } } #endregion @@ -448,5 +703,7 @@ public override void Dispose() } #endregion + + #endregion } } \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/V8JsEngineFactory.cs b/src/JavaScriptEngineSwitcher.V8/V8JsEngineFactory.cs new file mode 100644 index 00000000..2bfdbe03 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8/V8JsEngineFactory.cs @@ -0,0 +1,53 @@ +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.V8 +{ + /// + /// V8 JS engine factory + /// + public sealed class V8JsEngineFactory : IJsEngineFactory + { + /// + /// Settings of the V8 JS engine + /// + private readonly V8Settings _settings; + + + /// + /// Constructs an instance of the V8 JS engine factory + /// + public V8JsEngineFactory() + : this(new V8Settings()) + { } + + /// + /// Constructs an instance of the V8 JS engine factory + /// + /// Settings of the V8 JS engine + public V8JsEngineFactory(V8Settings settings) + { + _settings = settings; + } + + + #region IJsEngineFactory implementation + + /// + public string EngineName + { + get { return V8JsEngine.EngineName; } + } + + + /// + /// Creates a instance of the V8 JS engine + /// + /// Instance of the V8 JS engine + public IJsEngine CreateEngine() + { + return new V8JsEngine(_settings); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/V8PrecompiledScript.cs b/src/JavaScriptEngineSwitcher.V8/V8PrecompiledScript.cs new file mode 100644 index 00000000..16464dd5 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8/V8PrecompiledScript.cs @@ -0,0 +1,77 @@ +using OriginalCacheKind = Microsoft.ClearScript.V8.V8CacheKind; +using OriginalDocumentInfo = Microsoft.ClearScript.DocumentInfo; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.V8 +{ + /// + /// Represents a pre-compiled script that can be executed by different instances of the V8 JS engine + /// + internal sealed class V8PrecompiledScript : IPrecompiledScript + { + /// + /// Gets a source code of the script + /// + public string Code + { + get; + private set; + } + + /// + /// Gets a kind of cache data to be generated + /// + public OriginalCacheKind CacheKind + { + get; + private set; + } + + /// + /// Gets a cached data for accelerated recompilation + /// + public byte[] CachedBytes + { + get; + private set; + } + + /// + /// Gets a meta-information for the document + /// + public OriginalDocumentInfo DocumentInfo + { + get; + private set; + } + + + /// + /// Constructs an instance of pre-compiled script + /// + /// The source code of the script + /// The kind of cache data to be generated + /// Cached data for accelerated recompilation + /// Meta-information for the document + public V8PrecompiledScript(string code, OriginalCacheKind cacheKind, byte[] cachedBytes, + OriginalDocumentInfo documentInfo) + { + Code = code; + CacheKind = cacheKind; + CachedBytes = cachedBytes; + DocumentInfo = documentInfo; + } + + + #region IPrecompiledScript implementation + + /// + public string EngineName + { + get { return V8JsEngine.EngineName; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/V8Settings.cs b/src/JavaScriptEngineSwitcher.V8/V8Settings.cs new file mode 100644 index 00000000..7d2810f1 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8/V8Settings.cs @@ -0,0 +1,274 @@ +using System; + +namespace JavaScriptEngineSwitcher.V8 +{ + /// + /// Settings of the V8 JS engine + /// + public sealed class V8Settings + { + /// + /// Gets or sets a flag for whether to add the + /// Performance + /// object to the script engine's global namespace + /// + /// + /// This object provides a set of low-level native facilities for performance-sensitive scripts. + /// + public bool AddPerformanceObject + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to allow the usage of reflection API in the script code + /// + /// + /// This affects , , + /// and . + /// By default, any attempt to access these members from the script code will throw an exception. + /// + public bool AllowReflection + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to the script engine is to wait for a debugger connection + /// and schedule a pause before executing the first line of application script code + /// + /// + /// This property is ignored if value of the property is false. + /// + public bool AwaitDebuggerAndPauseOnStart + { + get; + set; + } + + /// + /// Gets or sets a TCP port on which to listen for a debugger connection + /// + public ushort DebugPort + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to disable dynamic method binding + /// + /// + /// When this property is set to true, the script engine bypasses the default method + /// binding algorithm and uses reflection-based method binding instead. This approach + /// abandons support for generic type inference and other features, but it avoids engaging + /// the dynamic infrastructure. + /// + public bool DisableDynamicBinding + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to disable global members + /// + public bool DisableGlobalMembers + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to enable script debugging features + /// (allows a TCP-based debugging) + /// + public bool EnableDebugging + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to enable remote script debugging + /// + /// + /// This property is ignored if value of the + /// property is false. + /// + public bool EnableRemoteDebugging + { + get; + set; + } + + /// + /// Gets or sets a heap expansion multiplier + /// + /// + /// When set to a value greater than 1, this property enables on-demand heap expansion, + /// which automatically increases the maximum heap size by the specified multiplier + /// whenever the script engine is close to exceeding the current limit. Note that a buggy + /// or malicious script can still cause an application to fail by exhausting its address + /// space or total available memory. On-demand heap expansion is recommended for use in + /// conjunction with heap size monitoring (see property to help + /// contain runaway scripts). + /// + public double HeapExpansionMultiplier + { + get; + set; + } + + /// + /// Gets or sets a minimum time interval between consecutive heap size samples + /// + /// + /// This property is effective only when heap size monitoring is enabled (see + /// property) + /// + public TimeSpan HeapSizeSampleInterval + { + get; + set; + } + + /// + /// Gets or sets a maximum amount of ArrayBuffer memory the runtime may allocate + /// + /// + /// This property is specified in bytes. ArrayBuffer memory is allocated outside + /// the runtime's heap and released when its garbage collector reclaims the corresponding + /// JavaScript ArrayBuffer object. Leave this property at its default value to + /// enforce no limit. + /// + public ulong MaxArrayBufferAllocation + { + get; + set; + } + + /// + /// Gets or sets a maximum size of the executable code heap in mebibytes + /// + [Obsolete("Executable code now occupies the old object heap. Use a `MaxOldSpaceSize` property instead.")] + public int MaxExecutableSize + { + get; + set; + } + + /// + /// Gets or sets a soft limit for the size of the V8 runtime's heap in bytes + /// + /// + /// + /// When it is set to the default value, heap size monitoring is disabled, and + /// scripts with memory leaks or excessive memory usage can cause unrecoverable + /// errors and process termination. + /// + /// + /// A V8 runtime unconditionally terminates the process when it exceeds its resource + /// constraints. This property enables external heap size monitoring that can prevent + /// termination in some scenarios. To be effective, it should be set to a value that + /// is significantly lower than property. Note that + /// enabling heap size monitoring results in slower script execution. + /// + /// + /// Exceeding this limit causes the V8 runtime to interrupt script execution and throw + /// an exception. + /// + /// + /// Note that ArrayBuffer memory is allocated outside the runtime's heap and is + /// therefore not tracked by heap size monitoring. See + /// property for additional information. + /// + /// + public UIntPtr MaxHeapSize + { + get; + set; + } + + /// + /// Gets or sets a maximum size of the new object heap in mebibytes + /// + public int MaxNewSpaceSize + { + get; + set; + } + + /// + /// Gets or sets a maximum size of the old object heap in mebibytes + /// + public int MaxOldSpaceSize + { + get; + set; + } + + /// + /// Gets or sets a maximum amount by which the V8 runtime is permitted to grow + /// the stack during script execution in bytes + /// + /// + /// + /// When it is set to the default value, no stack usage limit is enforced, and + /// scripts with unchecked recursion or other excessive stack usage can cause + /// unrecoverable errors and process termination. + /// + /// + /// Note that the V8 runtime does not monitor stack usage while a host call is in progress. + /// Monitoring is resumed when control returns to the runtime. + /// + /// + public UIntPtr MaxStackUsage + { + get; + set; + } + + /// + /// Gets or sets a flag for whether to set native timers to the highest available resolution + /// while the current script engine's instance is active + /// + /// + /// This property is ignored if value of the property + /// is false. It is only a hint and may be ignored on some systems. On platforms that + /// support it, this property can degrade overall system performance or power efficiency, so + /// caution is recommended. + /// + public bool SetTimerResolution + { + get; + set; + } + + + /// + /// Constructs an instance of the V8 settings + /// + public V8Settings() + { + AddPerformanceObject = false; + AllowReflection = false; + AwaitDebuggerAndPauseOnStart = false; + DebugPort = 9222; + DisableDynamicBinding = false; + DisableGlobalMembers = false; + EnableDebugging = false; + EnableRemoteDebugging = false; + HeapExpansionMultiplier = 0; + HeapSizeSampleInterval = TimeSpan.Zero; + MaxArrayBufferAllocation = ulong.MaxValue; + MaxHeapSize = UIntPtr.Zero; + MaxNewSpaceSize = 0; + MaxOldSpaceSize = 0; + MaxStackUsage = UIntPtr.Zero; + SetTimerResolution = false; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.V8/readme.txt b/src/JavaScriptEngineSwitcher.V8/readme.txt new file mode 100644 index 00000000..8e5be144 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.V8/readme.txt @@ -0,0 +1,40 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: V8 v3.29.1 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2025 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + JavaScriptEngineSwitcher.V8 contains a `V8JsEngine` adapter (wrapper for the + Microsoft ClearScript.V8 (http://github.com/Microsoft/ClearScript) version + 7.5). + + This package does not contain the native ClearScript.V8 assemblies. + Therefore, you need to choose and install the most appropriate package(s) for + your platform. The following packages are available: + + * Microsoft.ClearScript.V8.Native.win-x86 + * Microsoft.ClearScript.V8.Native.win-x64 + * Microsoft.ClearScript.V8.Native.win-arm64 + * Microsoft.ClearScript.V8.Native.linux-x64 + * Microsoft.ClearScript.V8.Native.linux-arm + * Microsoft.ClearScript.V8.Native.linux-arm64 + * Microsoft.ClearScript.V8.Native.osx-x64 + * Microsoft.ClearScript.V8.Native.osx-arm64 + + ============= + RELEASE NOTES + ============= + Performed a migration to a modern API for pre-compilation of scripts. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/Constants/DllName.cs b/src/JavaScriptEngineSwitcher.Vroom/Constants/DllName.cs new file mode 100644 index 00000000..6b464d65 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/Constants/DllName.cs @@ -0,0 +1,12 @@ +namespace JavaScriptEngineSwitcher.Vroom.Constants +{ + /// + /// DLL names + /// + internal static class DllName + { + public const string Universal = "VroomJsNative"; + public const string ForWindows = Universal + ".dll"; + public const string ForUnix = "lib" + Universal + ".so"; + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/JavaScriptEngineSwitcher.Vroom.csproj b/src/JavaScriptEngineSwitcher.Vroom/JavaScriptEngineSwitcher.Vroom.csproj new file mode 100644 index 00000000..8f6a4403 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/JavaScriptEngineSwitcher.Vroom.csproj @@ -0,0 +1,52 @@ + + + + JS Engine Switcher: Vroom + 3.24.1 + net40-client;net45;net471;netstandard1.6;netstandard2.0 + 1.6.0 + Library + true + $(NoWarn);CS1591;NETSDK1215;NU1903 + false + true + true + + + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Vroom_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_Vroom_Logo128x128.png + JavaScriptEngineSwitcher.Vroom contains a `VroomJsEngine` adapter (wrapper for the VroomJs). + $(PackageCommonTags);VroomJs;V8 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/JsEngineFactoryCollectionExtensions.cs b/src/JavaScriptEngineSwitcher.Vroom/JsEngineFactoryCollectionExtensions.cs new file mode 100644 index 00000000..ac917976 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/JsEngineFactoryCollectionExtensions.cs @@ -0,0 +1,79 @@ +using System; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Vroom +{ + /// + /// JS engine factory collection extensions + /// + public static class JsEngineFactoryCollectionExtensions + { + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Instance of + public static JsEngineFactoryCollection AddVroom(this JsEngineFactoryCollection source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return source.AddVroom(new VroomSettings()); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The delegate to configure the provided + /// Instance of + public static JsEngineFactoryCollection AddVroom(this JsEngineFactoryCollection source, + Action configure) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var settings = new VroomSettings(); + configure(settings); + + return source.AddVroom(settings); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Settings of the Vroom JS engine + /// Instance of + public static JsEngineFactoryCollection AddVroom(this JsEngineFactoryCollection source, + VroomSettings settings) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + source.Add(new VroomJsEngineFactory(settings)); + + return source; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.Vroom/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..a27c35de --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/PACKAGE-DESCRIPTION.md @@ -0,0 +1,3 @@ +JavaScriptEngineSwitcher.Vroom contains a `VroomJsEngine` adapter (wrapper for the [VroomJs](http://github.com/pauldotknopf/vroomjs-core) version 1.2.3 with support of V8 version 3.17.16.2). + +For correct working of the VroomJs on Windows require the [Visual C++ Redistributable for Visual Studio 2012](https://www.microsoft.com/en-us/download/details.aspx?id=30679) and [Microsoft Visual C++ 2015 Redistributable](https://www.microsoft.com/en-us/download/details.aspx?id=53840). \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/Resources/Strings.Designer.cs b/src/JavaScriptEngineSwitcher.Vroom/Resources/Strings.Designer.cs new file mode 100644 index 00000000..59e44bb5 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/Resources/Strings.Designer.cs @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +namespace JavaScriptEngineSwitcher.Vroom.Resources +{ + using System; + using System.Globalization; + using System.Reflection; + using System.Resources; + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + internal class Strings + { + private static Lazy _resourceManager = + new Lazy(() => new ResourceManager( + "JavaScriptEngineSwitcher.Vroom.Resources.Strings", +#if NET20 || NET30 || NET35 || NET40 + typeof(Strings).Assembly +#else + typeof(Strings).GetTypeInfo().Assembly +#endif + )); + + private static CultureInfo _resourceCulture; + + /// + /// Returns a cached ResourceManager instance used by this class + /// + internal static ResourceManager ResourceManager + { + get + { + return _resourceManager.Value; + } + } + + /// + /// Overrides a current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class + /// + internal static CultureInfo Culture + { + get + { + return _resourceCulture; + } + set + { + _resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to "Serialization of type `{0}` is not supported." + /// + internal static string Common_CannotSerializeType + { + get { return GetString("Common_CannotSerializeType"); } + } + + /// + /// Looks up a localized string similar to "You can build the '{0}' and '{1}' assemblies by using following instructions from official..." + /// + internal static string Engine_BuildNativeAssemblies + { + get { return GetString("Engine_BuildNativeAssemblies"); } + } + + /// + /// Looks up a localized string similar to "Try to install the Visual C++ Redistributable for Visual Studio 2012..." + /// + internal static string Engine_VcRedist2012And2015InstallationRequired + { + get { return GetString("Engine_VcRedist2012And2015InstallationRequired"); } + } + + private static string GetString(string name) + { + string value = ResourceManager.GetString(name, _resourceCulture); + + return value; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/Resources/Strings.resx b/src/JavaScriptEngineSwitcher.Vroom/Resources/Strings.resx new file mode 100644 index 00000000..bd6c89ee --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/Resources/Strings.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Serialization of type `{0}` is not supported. + + + You can build the '{0}' and '{1}' assemblies by using following instructions from official repository - {2}. + + + Try to install the Visual C++ Redistributable for Visual Studio 2012 (https://www.microsoft.com/en-us/download/details.aspx?id=30679) and the Microsoft Visual C++ 2015 Redistributable (https://www.microsoft.com/en-us/download/details.aspx?id=53840). + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/Resources/Strings.ru-RU.Designer.cs b/src/JavaScriptEngineSwitcher.Vroom/Resources/Strings.ru-RU.Designer.cs new file mode 100644 index 00000000..e69de29b diff --git a/src/JavaScriptEngineSwitcher.Vroom/Resources/Strings.ru-RU.resx b/src/JavaScriptEngineSwitcher.Vroom/Resources/Strings.ru-RU.resx new file mode 100644 index 00000000..ce7a9cf2 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/Resources/Strings.ru-RU.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Сериализация типа `{0}` не поддерживается! + + + Вы можете собрать "{0}" и "{1}", используя следующие инструкции из официального репозитория - {2}. + + + Попробуйте установить следующие распространяемые пакеты: Visual C++ для Visual Studio 2012 (https://www.microsoft.com/ru-ru/download/details.aspx?id=30679) и Microsoft Visual C++ 2015 (https://www.microsoft.com/ru-ru/download/details.aspx?id=53840). + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/Utilities/SimplisticJsSerializer.cs b/src/JavaScriptEngineSwitcher.Vroom/Utilities/SimplisticJsSerializer.cs new file mode 100644 index 00000000..4c29d4ce --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/Utilities/SimplisticJsSerializer.cs @@ -0,0 +1,180 @@ +using System; +using System.Globalization; +using System.Text; + +using AdvancedStringBuilder; + +using JavaScriptEngineSwitcher.Core; + +using JavaScriptEngineSwitcher.Vroom.Resources; + +namespace JavaScriptEngineSwitcher.Vroom.Utilities +{ + /// + /// Simplistic JavaScript serializer + /// + internal static class SimplisticJsSerializer + { + private const bool JsEncodeAmpersand = true; + + + /// + /// Converts a value to JavaScript string + /// + /// The value to serialize + /// The serialized JavaScript string + public static string Serialize(object value) + { + if (value == null) + { + return "null"; + } + + if (value is Undefined) + { + return "undefined"; + } + + string serializedValue; + Type type = value.GetType(); + TypeCode typeCode = Type.GetTypeCode(type); + + switch (typeCode) + { + case TypeCode.Boolean: + serializedValue = SerializeBoolean((bool)value); + break; + case TypeCode.Int32: + var convertible = value as IConvertible; + serializedValue = (convertible != null) ? + convertible.ToString(CultureInfo.InvariantCulture) : value.ToString(); + break; + case TypeCode.Double: + serializedValue = ((double)value).ToString("r", CultureInfo.InvariantCulture); + break; + case TypeCode.String: + serializedValue = SerializeString((string)value); + break; + default: + throw new NotSupportedException(string.Format(Strings.Common_CannotSerializeType, type)); + } + + return serializedValue; + } + + private static string SerializeBoolean(bool value) + { + string serializedValue = value ? "true" : "false"; + + return serializedValue; + } + + private static string SerializeString(string value) + { + string serializedValue = '"' + JsStringEncode(value) + '"'; + + return serializedValue; + } + + private static string JsStringEncode(string value) + { + if (string.IsNullOrEmpty(value)) + { + return string.Empty; + } + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder sb = null; + int charCount = value.Length; + + int startIndex = 0; + int count = 0; + + for (int charIndex = 0; charIndex < charCount; ++charIndex) + { + char charValue = value[charIndex]; + + if (CharRequiresJsEncoding(charValue)) + { + if (sb == null) + { + sb = stringBuilderPool.Rent(); + } + + if (count > 0) + { + sb.Append(value, startIndex, count); + } + + startIndex = charIndex + 1; + count = 0; + + switch (charValue) + { + case '\b': + sb.Append("\\b"); + break; + case '\t': + sb.Append("\\t"); + break; + case '\n': + sb.Append("\\n"); + break; + case '\f': + sb.Append("\\f"); + break; + case '\r': + sb.Append("\\r"); + break; + case '"': + sb.Append("\\\""); + break; + case '\\': + sb.Append("\\\\"); + break; + default: + AppendCharAsJsUnicode(sb, charValue); + break; + } + } + else + { + ++count; + } + } + + if (sb == null) + { + return value; + } + + if (count > 0) + { + sb.Append(value, startIndex, count); + } + + string encodedValue = sb.ToString(); + stringBuilderPool.Return(sb); + + return encodedValue; + } + + private static bool CharRequiresJsEncoding(char charValue) + { + if (charValue >= 32 && charValue != 34 && (charValue != 92 && charValue != 39) + && (charValue != 60 && charValue != 62 && (charValue != 38 || !JsEncodeAmpersand)) + && (charValue != 133 && charValue != 8232)) + { + return (charValue == 8233); + } + + return true; + } + + private static void AppendCharAsJsUnicode(StringBuilder sb, char charValue) + { + sb.Append("\\u"); + sb.Append(((int)charValue).ToString("x4", CultureInfo.InvariantCulture)); + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/VroomJsEngine.cs b/src/JavaScriptEngineSwitcher.Vroom/VroomJsEngine.cs new file mode 100644 index 00000000..270b9ccd --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/VroomJsEngine.cs @@ -0,0 +1,688 @@ +using System; +using System.Collections.Generic; +#if NET45_OR_GREATER || NETSTANDARD +using System.Runtime.InteropServices; +#endif +using System.Text; +using System.Text.RegularExpressions; + +using AdvancedStringBuilder; +#if NET40 +using PolyfillsForOldDotNet.System.Runtime.InteropServices; +#endif + +using OriginalAssemblyLoader = VroomJs.AssemblyLoader; +using OriginalContext = VroomJs.JsContext; +using OriginalEngine = VroomJs.JsEngine; +using OriginalException = VroomJs.JsException; +using OriginalInteropException = VroomJs.JsInteropException; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Constants; +using JavaScriptEngineSwitcher.Core.Extensions; +using JavaScriptEngineSwitcher.Core.Helpers; +using JavaScriptEngineSwitcher.Core.Utilities; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; +using WrapperCompilationException = JavaScriptEngineSwitcher.Core.JsCompilationException; +using WrapperEngineLoadException = JavaScriptEngineSwitcher.Core.JsEngineLoadException; +using WrapperException = JavaScriptEngineSwitcher.Core.JsException; +using WrapperInterruptedException = JavaScriptEngineSwitcher.Core.JsInterruptedException; +using WrapperRuntimeException = JavaScriptEngineSwitcher.Core.JsRuntimeException; +using WrapperScriptException = JavaScriptEngineSwitcher.Core.JsScriptException; + +using JavaScriptEngineSwitcher.Vroom.Constants; +using JavaScriptEngineSwitcher.Vroom.Resources; +using JavaScriptEngineSwitcher.Vroom.Utilities; + +namespace JavaScriptEngineSwitcher.Vroom +{ + /// + /// Adapter for the Vroom JS engine (cross-platform bridge to the V8 JS engine) + /// + public sealed class VroomJsEngine : JsEngineBase + { + /// + /// Name of JS engine + /// + public const string EngineName = "VroomJsEngine"; + + /// + /// Version of original JS engine + /// + private const string EngineVersion = "3.17.16.2"; + + /// + /// Regular expression for working with the script error message + /// + private static readonly Regex _scriptErrorMessageRegex = + new Regex(@"^" + CommonRegExps.DocumentNamePattern + ": " + + @"Uncaught " + + @"(?:" + CommonRegExps.JsFullNamePattern + @": )?" + + @"(?[\s\S]+?) " + + @"at line: \d+ column: \d+\.$"); + + /// + /// Vroom JS engine + /// + private OriginalEngine _jsEngine; + + /// + /// JS context + /// + private OriginalContext _jsContext; + + /// + /// Synchronizer of code execution + /// + private readonly object _executionSynchronizer = new object(); + + /// + /// List of host items + /// + private readonly Dictionary _hostItems = new Dictionary(); + + /// + /// Synchronizer of JS engine initialization + /// + private static readonly object _initializationSynchronizer = new object(); + + /// + /// Flag indicating whether the JS engine is initialized + /// + private static bool _initialized; + + + /// + /// Unique document name manager + /// + private readonly UniqueDocumentNameManager _documentNameManager = + new UniqueDocumentNameManager(DefaultDocumentName); + + + /// + /// Constructs an instance of adapter for the Vroom JS engine + /// (cross-platform bridge to the V8 JS engine) + /// + public VroomJsEngine() + : this(new VroomSettings()) + { } + + /// + /// Constructs an instance of adapter for the Vroom JS engine + /// (cross-platform bridge to the V8 JS engine) + /// + /// Settings of the Vroom JS engine + public VroomJsEngine(VroomSettings settings) + { + Initialize(); + + VroomSettings vroomSettings = settings ?? new VroomSettings(); + + try + { + _jsEngine = new OriginalEngine(vroomSettings.MaxYoungSpaceSize, vroomSettings.MaxOldSpaceSize); + _jsContext = _jsEngine.CreateContext(); + } + catch (TypeInitializationException e) + { + Exception innerException = e.InnerException; + if (innerException != null) + { + var dllNotFoundException = innerException as DllNotFoundException; + if (dllNotFoundException != null) + { + throw WrapDllNotFoundException(dllNotFoundException); + } + else + { + throw JsErrorHelpers.WrapEngineLoadException(innerException, EngineName, EngineVersion, + true); + } + } + + throw JsErrorHelpers.WrapEngineLoadException(e, EngineName, EngineVersion, true); + } + catch (Exception e) + { + throw JsErrorHelpers.WrapEngineLoadException(e, EngineName, EngineVersion, true); + } + finally + { + if (_jsContext == null) + { + Dispose(); + } + } + } + + + /// + /// Initializes a JS engine + /// + private static void Initialize() + { + if (_initialized) + { + return; + } + + lock (_initializationSynchronizer) + { + if (_initialized) + { + return; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + try + { + OriginalAssemblyLoader.EnsureLoaded(); + } + catch (Exception e) + { + throw WrapAssemblyLoaderException(e); + } + } + + _initialized = true; + } + } + + #region Mapping + + /// + /// Makes a mapping of value from the host type to a script type + /// + /// The source value + /// The mapped value + private static object MapToScriptType(object value) + { + if (value is Undefined) + { + return null; + } + + return value; + } + + private static WrapperException WrapJsException(OriginalException originalException) + { + WrapperException wrapperException; + string message = originalException.Message; + string description = message; + string type = originalException.Type; + string documentName = originalException.Resource; + int lineNumber = originalException.Line; + int columnNumber = originalException.Column; + + if (originalException is OriginalInteropException) + { + wrapperException = new WrapperException(message, EngineName, EngineVersion, originalException); + } + else if (type == null && message.Equals(": at line: 0 column: 1.", StringComparison.Ordinal)) + { + wrapperException = new WrapperInterruptedException(CoreStrings.Runtime_ScriptInterrupted, + EngineName, EngineVersion, originalException); + } + else + { + Match scriptErrorMessageMatch = _scriptErrorMessageRegex.Match(message); + if (scriptErrorMessageMatch.Success) + { + WrapperScriptException wrapperScriptException; + description = scriptErrorMessageMatch.Groups["description"].Value; + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, + lineNumber, columnNumber); + + if (type == JsErrorType.Syntax) + { + wrapperScriptException = new WrapperCompilationException(message, + EngineName, EngineVersion, originalException); + } + else + { + wrapperScriptException = new WrapperRuntimeException(message, + EngineName, EngineVersion, originalException); + } + wrapperScriptException.Type = type; + wrapperScriptException.DocumentName = documentName; + wrapperScriptException.LineNumber = lineNumber; + wrapperScriptException.ColumnNumber = columnNumber; + + wrapperException = wrapperScriptException; + + } + else + { + wrapperException = new WrapperException(message, EngineName, EngineVersion, + originalException); + } + } + + wrapperException.Description = description; + + return wrapperException; + } + + private static WrapperEngineLoadException WrapAssemblyLoaderException( + Exception originalAssemblyLoaderException) + { + string originalMessage = originalAssemblyLoaderException.Message; + string description; + string message; + Architecture osArchitecture = RuntimeInformation.OSArchitecture; + + if ((osArchitecture == Architecture.X64 || osArchitecture == Architecture.X86) + && originalMessage.StartsWith("Couldn't load native assembly at ")) + { + description = Strings.Engine_VcRedist2012And2015InstallationRequired; + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, EngineName); + } + else + { + description = originalMessage; + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, EngineName, true); + } + + var wrapperEngineLoadException = new WrapperEngineLoadException(message, EngineName, EngineVersion, + originalAssemblyLoaderException) + { + Description = description + }; + + return wrapperEngineLoadException; + } + + private static WrapperEngineLoadException WrapDllNotFoundException( + DllNotFoundException originalDllNotFoundException) + { + string originalMessage = originalDllNotFoundException.Message; + string description; + string message; + bool isMonoRuntime = Utils.IsMonoRuntime(); + + if ((isMonoRuntime && originalMessage == DllName.Universal) + || originalMessage.ContainsQuotedValue(DllName.Universal)) + { + const string buildInstructionsUrl = "https://github.com/pauldotknopf/vroomjs-core#maclinux"; + bool quoteDescription = false; + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder descriptionBuilder = stringBuilderPool.Rent(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Architecture osArchitecture = RuntimeInformation.OSArchitecture; + if (osArchitecture == Architecture.X64 || osArchitecture == Architecture.X86) + { + descriptionBuilder.Append(originalMessage); + quoteDescription = true; + } + else + { + descriptionBuilder.AppendFormat(CoreStrings.Engine_ProcessorArchitectureNotSupported, + osArchitecture.ToString().ToLowerInvariant()); + } + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + descriptionBuilder.AppendFormat(CoreStrings.Engine_AssemblyNotFound, DllName.ForUnix); + descriptionBuilder.Append(" "); + descriptionBuilder.AppendFormat(Strings.Engine_BuildNativeAssemblies, DllName.ForUnix, + "libv8.so." + EngineVersion, buildInstructionsUrl); + } + else + { + descriptionBuilder.Append(CoreStrings.Engine_OperatingSystemNotSupported); + } + + description = descriptionBuilder.ToString(); + stringBuilderPool.Return(descriptionBuilder); + + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, EngineName, + quoteDescription); + } + else + { + description = originalMessage; + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, EngineName, true); + } + + var wrapperEngineLoadException = new WrapperEngineLoadException(message, EngineName, EngineVersion, + originalDllNotFoundException) + { + Description = description + }; + + return wrapperEngineLoadException; + } + + #endregion + + #region JsEngineBase overrides + + protected override IPrecompiledScript InnerPrecompile(string code) + { + throw new NotSupportedException(); + } + + protected override IPrecompiledScript InnerPrecompile(string code, string documentName) + { + throw new NotSupportedException(); + } + + protected override object InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override object InnerEvaluate(string expression, string documentName) + { + object result; + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + + lock (_executionSynchronizer) + { + try + { + result = _jsContext.Execute(expression, uniqueDocumentName); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + + return result; + } + + protected override T InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override T InnerEvaluate(string expression, string documentName) + { + object result = InnerEvaluate(expression, documentName); + + return TypeConverter.ConvertToType(result); + } + + protected override void InnerExecute(string code) + { + InnerExecute(code, null); + } + + protected override void InnerExecute(string code, string documentName) + { + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + + lock (_executionSynchronizer) + { + try + { + _jsContext.Execute(code, uniqueDocumentName); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + } + + protected override void InnerExecute(IPrecompiledScript precompiledScript) + { + throw new NotSupportedException(); + } + + protected override object InnerCallFunction(string functionName, params object[] args) + { + string functionCallExpression; + int argumentCount = args.Length; + + if (argumentCount > 0) + { + string[] serializedArgs = new string[argumentCount]; + + for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) + { + object value = args[argumentIndex]; + string serializedValue = SimplisticJsSerializer.Serialize(value); + + serializedArgs[argumentIndex] = serializedValue; + } + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder functionCallBuilder = stringBuilderPool.Rent(); + functionCallBuilder.Append(functionName); + functionCallBuilder.Append("("); + + for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) + { + string serializedValue = serializedArgs[argumentIndex]; + + if (argumentIndex > 0) + { + functionCallBuilder.Append(", "); + } + functionCallBuilder.Append(serializedValue); + } + + functionCallBuilder.Append(");"); + + functionCallExpression = functionCallBuilder.ToString(); + stringBuilderPool.Return(functionCallBuilder); + } + else + { + functionCallExpression = string.Format("{0}();", functionName); + } + + object result = Evaluate(functionCallExpression); + + return result; + } + + protected override T InnerCallFunction(string functionName, params object[] args) + { + object result = InnerCallFunction(functionName, args); + + return TypeConverter.ConvertToType(result); + } + + protected override bool InnerHasVariable(string variableName) + { + string expression = string.Format("(typeof {0} !== 'undefined');", variableName); + var result = InnerEvaluate(expression); + + return result; + } + + protected override object InnerGetVariableValue(string variableName) + { + object result; + + lock (_executionSynchronizer) + { + try + { + result = _jsContext.GetVariable(variableName); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + + return result; + } + + protected override T InnerGetVariableValue(string variableName) + { + object result = InnerGetVariableValue(variableName); + + return TypeConverter.ConvertToType(result); + } + + protected override void InnerSetVariableValue(string variableName, object value) + { + object processedValue = MapToScriptType(value); + + lock (_executionSynchronizer) + { + try + { + _jsContext.SetVariable(variableName, processedValue); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + } + + protected override void InnerRemoveVariable(string variableName) + { + string expression = string.Format(@"if (typeof {0} !== 'undefined') {{ + {0} = undefined; +}}", variableName); + + lock (_executionSynchronizer) + { + try + { + _jsContext.Execute(expression); + + if (_hostItems.ContainsKey(variableName)) + { + _hostItems.Remove(variableName); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + } + + protected override void InnerEmbedHostObject(string itemName, object value) + { + object processedValue = MapToScriptType(value); + InnerEmbedHostItem(itemName, processedValue); + } + + protected override void InnerEmbedHostType(string itemName, Type type) + { + InnerEmbedHostItem(itemName, type); + } + + private void InnerEmbedHostItem(string itemName, object value) + { + lock (_executionSynchronizer) + { + object oldValue = null; + if (_hostItems.ContainsKey(itemName)) + { + oldValue = _hostItems[itemName]; + } + _hostItems[itemName] = value; + + try + { + var delegateValue = value as Delegate; + if (delegateValue != null) + { + _jsContext.SetFunction(itemName, delegateValue); + } + else + { + _jsContext.SetVariable(itemName, value); + } + } + catch (OriginalException e) + { + if (oldValue != null) + { + _hostItems[itemName] = oldValue; + } + else + { + _hostItems.Remove(itemName); + } + + throw WrapJsException(e); + } + } + } + + protected override void InnerInterrupt() + { + _jsEngine.TerminateExecution(); + } + + protected override void InnerCollectGarbage() + { + throw new NotSupportedException(); + } + + #region IJsEngine implementation + + public override string Name + { + get { return EngineName; } + } + + public override string Version + { + get { return EngineVersion; } + } + + public override bool SupportsScriptPrecompilation + { + get { return false; } + } + + public override bool SupportsScriptInterruption + { + get { return true; } + } + + public override bool SupportsGarbageCollection + { + get { return false; } + } + + #endregion + + #region IDisposable implementation + + public override void Dispose() + { + if (_disposedFlag.Set()) + { + lock (_executionSynchronizer) + { + if (_jsContext != null) + { + _jsContext.Dispose(); + _jsContext = null; + } + + if (_jsEngine != null) + { + _jsEngine.Dispose(); + _jsEngine = null; + } + + _hostItems?.Clear(); + } + } + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/VroomJsEngineFactory.cs b/src/JavaScriptEngineSwitcher.Vroom/VroomJsEngineFactory.cs new file mode 100644 index 00000000..f6f31e4b --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/VroomJsEngineFactory.cs @@ -0,0 +1,53 @@ +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Vroom +{ + /// + /// Vroom JS engine factory + /// + public sealed class VroomJsEngineFactory : IJsEngineFactory + { + /// + /// Settings of the Vroom JS engine + /// + private readonly VroomSettings _settings; + + + /// + /// Constructs an instance of the Vroom JS engine factory + /// + public VroomJsEngineFactory() + : this(new VroomSettings()) + { } + + /// + /// Constructs an instance of the Vroom JS engine factory + /// + /// Settings of the Vroom JS engine + public VroomJsEngineFactory(VroomSettings settings) + { + _settings = settings; + } + + + #region IJsEngineFactory implementation + + /// + public string EngineName + { + get { return VroomJsEngine.EngineName; } + } + + + /// + /// Creates a instance of the Vroom JS engine + /// + /// Instance of the Vroom JS engine + public IJsEngine CreateEngine() + { + return new VroomJsEngine(_settings); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/VroomSettings.cs b/src/JavaScriptEngineSwitcher.Vroom/VroomSettings.cs new file mode 100644 index 00000000..7b4854dd --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/VroomSettings.cs @@ -0,0 +1,36 @@ +namespace JavaScriptEngineSwitcher.Vroom +{ + /// + /// Settings of the Vroom JS engine + /// + public sealed class VroomSettings + { + /// + /// Gets or sets a maximum size of the young object heap in bytes + /// + public int MaxYoungSpaceSize + { + get; + set; + } + + /// + /// Gets or sets a maximum size of the old object heap in bytes + /// + public int MaxOldSpaceSize + { + get; + set; + } + + + /// + /// Constructs an instance of the Vroom settings + /// + public VroomSettings() + { + MaxYoungSpaceSize = -1; + MaxOldSpaceSize = -1; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Vroom/readme.txt b/src/JavaScriptEngineSwitcher.Vroom/readme.txt new file mode 100644 index 00000000..b37c965c --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Vroom/readme.txt @@ -0,0 +1,34 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: Vroom v3.24.1 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2024 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + JavaScriptEngineSwitcher.Vroom contains a `VroomJsEngine` adapter (wrapper for the + VroomJs (http://github.com/pauldotknopf/vroomjs-core) version 1.2.3 with support + of V8 version 3.17.16.2). + + For correct working of the VroomJs on Windows require the Visual C++ + Redistributable for Visual Studio 2012 and the Microsoft Visual C++ 2015 + Redistributable. + + ==================== + POST-INSTALL ACTIONS + ==================== + If in your system does not have the `msvcr110.dll` and `msvcp140.dll` assemblies, + then download and install the Visual C++ Redistributable Packages for Visual + Studio 2012 (https://www.microsoft.com/en-us/download/details.aspx?id=30679) and + 2015 (https://www.microsoft.com/en-us/download/details.aspx?id=53840). + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Yantra/Helpers/ReflectionHelpers.cs b/src/JavaScriptEngineSwitcher.Yantra/Helpers/ReflectionHelpers.cs new file mode 100644 index 00000000..58dcdffd --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Yantra/Helpers/ReflectionHelpers.cs @@ -0,0 +1,48 @@ +using System; +using System.Reflection; + +using JavaScriptEngineSwitcher.Core.Utilities; + +namespace JavaScriptEngineSwitcher.Yantra.Helpers +{ + /// + /// Reflection helpers + /// + internal static class ReflectionHelpers + { + public static void FixArgumentTypes(ref object[] argValues, ParameterInfo[] parameters) + { + int argCount = argValues.Length; + int parameterCount = parameters.Length; + + for (int argIndex = 0; argIndex < argCount; argIndex++) + { + if (argIndex >= parameterCount) + { + break; + } + + object argValue = argValues[argIndex]; + if (argValue == null) + { + continue; + } + + Type argType = argValue.GetType(); + + ParameterInfo parameter = parameters[argIndex]; + Type parameterType = parameter.ParameterType; + + if (argType != parameterType) + { + object convertedArgValue; + + if (TypeConverter.TryConvertToType(argValue, parameterType, out convertedArgValue)) + { + argValues[argIndex] = convertedArgValue; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Yantra/Helpers/YantraJsErrorHelpers.cs b/src/JavaScriptEngineSwitcher.Yantra/Helpers/YantraJsErrorHelpers.cs new file mode 100644 index 00000000..cabb5f50 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Yantra/Helpers/YantraJsErrorHelpers.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.RegularExpressions; + +using JavaScriptEngineSwitcher.Core.Extensions; +using JavaScriptEngineSwitcher.Core.Helpers; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; + +namespace JavaScriptEngineSwitcher.Yantra.Helpers +{ + /// + /// JS error helpers + /// + internal static class YantraJsErrorHelpers + { + #region Error location + + private const string OriginalGlobalCode = "native"; + private const string OriginalAnonymousFunctionName = "inline"; + private const string WrapperGlobalCode = "Global code"; + private const string WrapperAnonymousFunctionName = "Anonymous function"; + + /// + /// Regular expression for working with line of the script error location + /// + private static readonly Regex _errorLocationLineRegex = + new Regex(@"^[ ]{4}at " + + @"(?" + + @"[\w][\w ]*" + + @"|" + + CommonRegExps.JsFullNamePattern + + @")" + + @":(?" + CommonRegExps.DocumentNamePattern + @")" + + @":(?\d+),(?\d+)$") + ; + + + /// + /// Parses a string representation of the script error location to produce an array of + /// instances + /// + /// String representation of the script error location + /// An array of instances + public static ErrorLocationItem[] ParseErrorLocation(string errorLocation) + { + if (string.IsNullOrWhiteSpace(errorLocation)) + { + return new ErrorLocationItem[0]; + } + + var errorLocationItems = new List(); + string[] lines = errorLocation.SplitToLines(); + int lineCount = lines.Length; + + for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) + { + string line = lines[lineIndex]; + if (line.Length == 0) + { + continue; + } + + Match lineMatch = _errorLocationLineRegex.Match(line); + + if (lineMatch.Success) + { + GroupCollection lineGroups = lineMatch.Groups; + + var errorLocationItem = new ErrorLocationItem + { + FunctionName = lineGroups["functionName"].Value, + DocumentName = lineGroups["documentName"].Value, + LineNumber = int.Parse(lineGroups["lineNumber"].Value), + ColumnNumber = int.Parse(lineGroups["columnNumber"].Value) + }; + errorLocationItems.Add(errorLocationItem); + } + else + { + Debug.WriteLine(string.Format(CoreStrings.Runtime_InvalidErrorLocationLineFormat, line)); + return new ErrorLocationItem[0]; + } + } + + return errorLocationItems.ToArray(); + } + + /// + /// Filters a error location items + /// + /// An array of instances + public static ErrorLocationItem[] FilterErrorLocationItems(ErrorLocationItem[] errorLocationItems) + { + int itemCount = errorLocationItems.Length; + if (itemCount == 0) + { + return errorLocationItems; + } + + int itemIndex = 0; + + while (itemIndex < itemCount) + { + ErrorLocationItem item = errorLocationItems[itemIndex]; + string documentName = item.DocumentName; + + if (documentName.StartsWith("/home/runner/work/yantra/")) + { + break; + } + + itemIndex++; + } + + if (itemIndex == itemCount) + { + return errorLocationItems; + } + + int firstSuitableItemIndex = itemIndex + 1; + int suitableItemCount = itemCount - firstSuitableItemIndex; + + var processedErrorLocationItems = new ErrorLocationItem[suitableItemCount]; + Array.Copy(errorLocationItems, firstSuitableItemIndex, processedErrorLocationItems, 0, suitableItemCount); + + return processedErrorLocationItems; + } + + /// + /// Fixes a error location items + /// + /// An array of instances + public static void FixErrorLocationItems(ErrorLocationItem[] errorLocationItems) + { + foreach (ErrorLocationItem errorLocationItem in errorLocationItems) + { + string functionName = errorLocationItem.FunctionName; + if (functionName == OriginalGlobalCode) + { + errorLocationItem.FunctionName = WrapperGlobalCode; + } + else if (functionName == OriginalAnonymousFunctionName) + { + errorLocationItem.FunctionName = WrapperAnonymousFunctionName; + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Yantra/JavaScriptEngineSwitcher.Yantra.csproj b/src/JavaScriptEngineSwitcher.Yantra/JavaScriptEngineSwitcher.Yantra.csproj new file mode 100644 index 00000000..2453325e --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Yantra/JavaScriptEngineSwitcher.Yantra.csproj @@ -0,0 +1,35 @@ + + + + JS Engine Switcher: Yantra + 3.30.2 + netstandard2.0;netstandard2.1 + Library + true + $(NoWarn);CS1591 + false + true + + + + + + + https://raw.githubusercontent.com/Taritsyn/JavaScriptEngineSwitcher/master/Icons/JavaScriptEngineSwitcher_Yantra_Logo128x128.png + ../../Icons/JavaScriptEngineSwitcher_Yantra_Logo128x128.png + JavaScriptEngineSwitcher.Yantra contains a `YantraJsEngine` adapter (wrapper for the YantraJS). + $(PackageCommonTags);Yantra;YantraJS + YantraJS was updated to version 1.2.293. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Yantra/JsEngineFactoryCollectionExtensions.cs b/src/JavaScriptEngineSwitcher.Yantra/JsEngineFactoryCollectionExtensions.cs new file mode 100644 index 00000000..8cb96eae --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Yantra/JsEngineFactoryCollectionExtensions.cs @@ -0,0 +1,78 @@ +using System; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Yantra +{ + /// + /// JS engine factory collection extensions + /// + public static class JsEngineFactoryCollectionExtensions + { + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Instance of + public static JsEngineFactoryCollection AddYantra(this JsEngineFactoryCollection source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return source.AddYantra(new YantraSettings()); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// The delegate to configure the provided + /// Instance of + public static JsEngineFactoryCollection AddYantra(this JsEngineFactoryCollection source, + Action configure) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + var settings = new YantraSettings(); + configure(settings); + + return source.AddYantra(settings); + } + + /// + /// Adds a instance of to + /// the specified + /// + /// Instance of + /// Settings of the Yantra JS engine + /// Instance of + public static JsEngineFactoryCollection AddYantra(this JsEngineFactoryCollection source, YantraSettings settings) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + source.Add(new YantraJsEngineFactory(settings)); + + return source; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Yantra/PACKAGE-DESCRIPTION.md b/src/JavaScriptEngineSwitcher.Yantra/PACKAGE-DESCRIPTION.md new file mode 100644 index 00000000..74bb8b43 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Yantra/PACKAGE-DESCRIPTION.md @@ -0,0 +1 @@ +JavaScriptEngineSwitcher.Yantra contains a `YantraJsEngine` adapter (wrapper for the [YantraJS](https://github.com/yantrajs/yantra) version 1.2.293). \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Yantra/YantraEngineFactory.cs b/src/JavaScriptEngineSwitcher.Yantra/YantraEngineFactory.cs new file mode 100644 index 00000000..566a0dad --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Yantra/YantraEngineFactory.cs @@ -0,0 +1,53 @@ +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Yantra +{ + /// + /// Yantra JS engine factory + /// + public sealed class YantraJsEngineFactory : IJsEngineFactory + { + /// + /// Settings of the Yantra JS engine + /// + private readonly YantraSettings _settings; + + + /// + /// Constructs an instance of the Yantra JS engine factory + /// + public YantraJsEngineFactory() + : this(new YantraSettings()) + { } + + /// + /// Constructs an instance of the Yantra JS engine factory + /// + /// Settings of the Yantra JS engine + public YantraJsEngineFactory(YantraSettings settings) + { + _settings = settings; + } + + + #region IJsEngineFactory implementation + + /// + public string EngineName + { + get { return YantraJsEngine.EngineName; } + } + + + /// + /// Creates a instance of the Yantra JS engine + /// + /// Instance of the Yantra JS engine + public IJsEngine CreateEngine() + { + return new YantraJsEngine(_settings); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Yantra/YantraJsConsoleCallback.cs b/src/JavaScriptEngineSwitcher.Yantra/YantraJsConsoleCallback.cs new file mode 100644 index 00000000..9351153a --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Yantra/YantraJsConsoleCallback.cs @@ -0,0 +1,9 @@ +namespace JavaScriptEngineSwitcher.Yantra +{ + /// + /// The JS debugging console callback + /// + /// Type of message + /// A array of objects to output + public delegate void YantraJsConsoleCallback(string type, object[] args); +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Yantra/YantraJsEngine.cs b/src/JavaScriptEngineSwitcher.Yantra/YantraJsEngine.cs new file mode 100644 index 00000000..8b812b65 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Yantra/YantraJsEngine.cs @@ -0,0 +1,799 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; + +using YantraJS.Core; + +using OriginalArguments = YantraJS.Core.Arguments; +using OriginalClrMemberNamingConvention = YantraJS.Core.Clr.ClrMemberNamingConvention; +using OriginalClrProxy = YantraJS.Core.Clr.ClrProxy; +using OriginalClrType = YantraJS.Core.Clr.ClrType; +using OriginalContext = YantraJS.Core.JSContext; +using OriginalDate = YantraJS.Core.JSDate; +using OriginalError = YantraJS.Core.JSError; +using OriginalException = YantraJS.Core.JSException; +using OriginalFunction = YantraJS.Core.JSFunction; +using OriginalJsonObject = YantraJS.Core.JSJSON; +using OriginalTypeConverter = YantraJS.Utils.TypeConverter; +using OriginalUndefined = YantraJS.Core.JSUndefined; +using OriginalValue = YantraJS.Core.JSValue; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Constants; +using JavaScriptEngineSwitcher.Core.Extensions; +using JavaScriptEngineSwitcher.Core.Helpers; +using JavaScriptEngineSwitcher.Core.Utilities; + +using CoreStrings = JavaScriptEngineSwitcher.Core.Resources.Strings; +using WrapperCompilationException = JavaScriptEngineSwitcher.Core.JsCompilationException; +using WrapperException = JavaScriptEngineSwitcher.Core.JsException; +using WrapperRuntimeException = JavaScriptEngineSwitcher.Core.JsRuntimeException; +using WrapperScriptException = JavaScriptEngineSwitcher.Core.JsScriptException; + +using JavaScriptEngineSwitcher.Yantra.Helpers; + +namespace JavaScriptEngineSwitcher.Yantra +{ + /// + /// Adapter for the Yantra JS engine + /// + public sealed class YantraJsEngine : JsEngineBase + { + /// + /// Name of JS engine + /// + public const string EngineName = "YantraJsEngine"; + + /// + /// Version of original JS engine + /// + private const string EngineVersion = "1.2.293"; + + /// + /// Regular expression for working with the error message + /// + private static readonly Regex _errorMessageRegex = + new Regex(@"^(?" + CommonRegExps.JsFullNamePattern + @"):\s+(?[\s\S]+?)" + + @"(?: at (?\d+), (?\d+))?$"); + + /// + /// Yantra JS context + /// + private OriginalContext _jsContext; + + /// + /// JS debugging console callback + /// + private YantraJsConsoleCallback _consoleCallback; + + /// + /// Synchronizer of code execution + /// + private readonly object _executionSynchronizer = new object(); + + /// + /// Unique document name manager + /// + private readonly UniqueDocumentNameManager _documentNameManager = + new UniqueDocumentNameManager(DefaultDocumentName); + + + /// + /// Constructs an instance of adapter for the Yantra JS engine + /// + public YantraJsEngine() + : this(new YantraSettings()) + { } + + /// + /// Constructs an instance of adapter for the Yantra JS engine + /// + /// Settings of the Yantra JS engine + public YantraJsEngine(YantraSettings settings) + { + YantraSettings yantraSettings = settings ?? new YantraSettings(); + _consoleCallback = yantraSettings.ConsoleCallback; + + try + { + _jsContext = new OriginalContext() + { + ClrMemberNamingConvention = OriginalClrMemberNamingConvention.Declared, + Debugger = yantraSettings.Debugger + }; + + if (_consoleCallback != null) + { + _jsContext.ConsoleEvent += OnConsoleWrite; + } + } + catch (Exception e) + { + throw JsErrorHelpers.WrapEngineLoadException(e, EngineName, EngineVersion, true); + } + } + + + #region Mapping + + /// + /// Makes a mapping of value from the host type to a script type + /// + /// The source value + /// The mapped value + private static OriginalValue MapToScriptType(object value) + { + if (value is Undefined) + { + return OriginalUndefined.Value; + } + + return OriginalTypeConverter.FromBasic(value); + } + + /// + /// Makes a mapping of array items from the host type to a script type + /// + /// The source array + /// The mapped array + private static OriginalValue[] MapToScriptType(object[] args) + { + return args.Select(MapToScriptType).ToArray(); + } + + /// + /// Makes a mapping of value from the script type to a host type + /// + /// The source value + /// The mapped value + private static object MapToHostType(OriginalValue value) + { + object result = value; + + if (value.IsNull) + { + result = null; + } + else if (value.IsUndefined) + { + result = Undefined.Value; + } + else if (value.IsBoolean) + { + result = value.BooleanValue; + } + else if (value.IsNumber) + { + result = value.DoubleValue; + } + else if (value.IsString) + { + result = value.ToString(); + } + else if (value.IsFunction && value is OriginalClrType) + { + result = value.ForceConvert(typeof(Type)); + } + else if (value.IsObject) + { + if (value is OriginalDate) + { + result = value.ForceConvert(typeof(DateTime)); + } + else if (value is OriginalClrProxy) + { + var clrProxy = (OriginalClrProxy)value; + result = clrProxy.Target; + } + } + + return result; + } + + /// + /// Makes a mapping of value from the script type to a host type + /// + /// The type to convert the value to + /// The source value + /// The mapped value + private static T MapToHostType(OriginalValue value) + { + if (value.IsNull) + { + return TypeConverter.ConvertToType(null); + } + + Type targetType = typeof(T); + + if (targetType == typeof(Undefined)) + { + if (value.IsUndefined) + { + return (T)(object)Undefined.Value; + } + else + { + throw new InvalidOperationException( + string.Format(CoreStrings.Common_CannotConvertObjectToType, value.GetType(), targetType) + ); + } + } + + T result; + + if (!value.ConvertTo(out result)) + { + if (targetType == typeof(string)) + { + result = (T)(object)value.ToString(); + } + else + { + throw new InvalidOperationException( + string.Format(CoreStrings.Common_CannotConvertObjectToType, value.GetType(), targetType) + ); + } + } + + return result; + } + + private static OriginalFunction CreateEmbeddedFunction(Delegate del) + { + var originalFunction = new OriginalFunction((in OriginalArguments args) => + { + MethodInfo method = del.GetMethodInfo(); + ParameterInfo[] parameters = method.GetParameters(); + object[] processedArgs = GetHostDelegateArguments(args, parameters.Length); + + ReflectionHelpers.FixArgumentTypes(ref processedArgs, parameters); + + object result; + + try + { + result = del.DynamicInvoke(processedArgs); + } + catch (Exception e) when ((e is TargetInvocationException || e is WrapperException) + && e.InnerException != null) + { + OriginalException originalException = OriginalException.From(e.InnerException); + throw originalException; + } + catch (Exception e) + { + OriginalException originalException = OriginalException.From(e); + throw originalException; + } + + OriginalValue resultValue = MapToScriptType(result); + + return resultValue; + }); + + return originalFunction; + } + + private static object[] GetHostDelegateArguments(in OriginalArguments args, int maxArgCount) + { + int argCount = args.Length; + if (argCount == 0) + { + return new object[0]; + } + + int processedArgCount = Math.Min(argCount, maxArgCount); + var processedArgs = new object[processedArgCount]; + + for (int argIndex = 0; argIndex < processedArgCount; argIndex++) + { + OriginalValue arg = args.GetAt(argIndex); + processedArgs[argIndex] = MapToHostType(arg); + } + + return processedArgs; + } + + private WrapperException WrapJsException(OriginalException originalException) + { + WrapperException wrapperException; + string message = originalException.Message; + string description = message; + string type = string.Empty; + string documentName = string.Empty; + int lineNumber = 0; + int columnNumber = 0; + ErrorLocationItem[] callStackItems = null; + + var errorValue = originalException.Error as OriginalError; + if (errorValue != null) + { + string messageWithType = errorValue.ToString(); + Match messageMatch = _errorMessageRegex.Match(messageWithType); + + if (messageMatch.Success) + { + GroupCollection messageGroups = messageMatch.Groups; + type = messageGroups["type"].Value; + description = messageGroups["description"].Value; + lineNumber = messageGroups["lineNumber"].Success ? + int.Parse(messageGroups["lineNumber"].Value) : 0; + columnNumber = messageGroups["columnNumber"].Success ? + int.Parse(messageGroups["columnNumber"].Value) : 0; + } + + string messageWithCallStack = type == JsErrorType.Syntax ? + originalException.JSStackTrace.AsStringOrDefault() + : + errorValue.Stack ?? errorValue["stack"].AsStringOrDefault() + ; + string rawCallStack = GetRawCallStack(message, messageWithType, messageWithCallStack); + + callStackItems = YantraJsErrorHelpers.ParseErrorLocation(rawCallStack); + callStackItems = YantraJsErrorHelpers.FilterErrorLocationItems(callStackItems); + YantraJsErrorHelpers.FixErrorLocationItems(callStackItems); + + if (callStackItems.Length > 0) + { + ErrorLocationItem firstCallStackItem = callStackItems[0]; + + documentName = firstCallStackItem.DocumentName; + if (lineNumber == 0 && columnNumber == 0) + { + lineNumber = firstCallStackItem.LineNumber; + columnNumber = firstCallStackItem.ColumnNumber; + } + } + } + + if (!string.IsNullOrWhiteSpace(type)) + { + WrapperScriptException wrapperScriptException; + if (type == JsErrorType.Syntax) + { + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, + lineNumber, columnNumber); + + wrapperScriptException = new WrapperCompilationException(message, EngineName, EngineVersion, + originalException); + } + else + { + string callStack = callStackItems != null ? + JsErrorHelpers.StringifyErrorLocationItems(callStackItems) : string.Empty; + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, callStack); + + var wrapperRuntimeException = new WrapperRuntimeException(message, EngineName, EngineVersion, + originalException); + wrapperRuntimeException.CallStack = callStack; + + wrapperScriptException = wrapperRuntimeException; + } + wrapperScriptException.Type = type; + wrapperScriptException.DocumentName = documentName; + wrapperScriptException.LineNumber = lineNumber; + wrapperScriptException.ColumnNumber = columnNumber; + + wrapperException = wrapperScriptException; + } + else + { + wrapperException = new WrapperException(message, EngineName, EngineVersion, originalException); + } + + wrapperException.Description = description; + + return wrapperException; + } + + private static string GetRawCallStack(string message, string messageWithType, string messageWithCallStack) + { + string baseMessage = messageWithCallStack.StartsWith(messageWithType) ? messageWithType : message; + string rawCallStack = messageWithCallStack + .TrimStart(baseMessage) + .TrimStart(new char[] { '\n', '\r' }) + ; + + return rawCallStack; + } + + private void OnConsoleWrite(OriginalContext context, string type, in OriginalArguments args) + { + int argCount = args.Length; + var processedArgs = new object[argCount]; + + for (int argIndex = 0; argIndex < argCount; argIndex++) + { + OriginalValue arg = args.GetAt(argIndex); + object processedArg = MapToHostType(arg); + + if (processedArg is OriginalValue) + { + if (arg.IsSymbol) + { + processedArg = string.Format("Symbol({0})", arg.ToString()); + } + else if (arg.IsFunction) + { + var jsFunction = (OriginalFunction)arg; + processedArg = string.Format("[Function: {0}]", jsFunction.name); + } + else if (arg.IsObject) + { + processedArg = OriginalJsonObject.Stringify(arg); + } + else + { + processedArg = arg.ToString(); + } + } + + processedArgs[argIndex] = processedArg; + } + + _consoleCallback?.Invoke(type, processedArgs); + } + + #endregion + + /// + /// Evaluates an expression without converting its result to a host type + /// + /// JS expression + /// Document name + /// Result of the expression not converted to a host type + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private OriginalValue InnerEvaluateWithoutResultConversion(string expression, string documentName) + { + OriginalValue resultValue; + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + + try + { + lock (_executionSynchronizer) + { + resultValue = _jsContext.Eval(expression, uniqueDocumentName); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + catch (Exception e) when ((e is TargetInvocationException || e is WrapperException) + && e.InnerException != null) + { + OriginalException originalException = OriginalException.From(e.InnerException); + throw originalException; + } + + return resultValue; + } + + /// + /// Calls a function without converting its result to a host type + /// + /// Function name + /// Function arguments + /// Result of the function execution not converted to a host type + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private OriginalValue InnerCallFunctionWithoutResultConversion(string functionName, params object[] args) + { + OriginalValue resultValue; + OriginalValue[] processedArgs = MapToScriptType(args); + + try + { + lock (_executionSynchronizer) + { + resultValue = _jsContext.InvokeMethod(functionName, new OriginalArguments(_jsContext, processedArgs)); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + catch (Exception e) when ((e is TargetInvocationException || e is WrapperException) + && e.InnerException != null) + { + OriginalException originalException = OriginalException.From(e.InnerException); + throw originalException; + } + + return resultValue; + } + + /// + /// Gets a value of variable without converting it to a host type + /// + /// Variable name + /// Value of variable not converted to a host type + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private OriginalValue InnerGetVariableValueWithoutResultConversion(string variableName) + { + OriginalValue variableValue; + + try + { + lock (_executionSynchronizer) + { + variableValue = _jsContext[variableName]; + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + + return variableValue; + } + + #region JsEngineBase overrides + + protected override IPrecompiledScript InnerPrecompile(string code) + { + throw new NotSupportedException(); + } + + protected override IPrecompiledScript InnerPrecompile(string code, string documentName) + { + throw new NotSupportedException(); + } + + protected override object InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override object InnerEvaluate(string expression, string documentName) + { + OriginalValue resultValue = InnerEvaluateWithoutResultConversion(expression, documentName); + object result = MapToHostType(resultValue); + + return result; + } + + protected override T InnerEvaluate(string expression) + { + return InnerEvaluate(expression, null); + } + + protected override T InnerEvaluate(string expression, string documentName) + { + OriginalValue resultValue = InnerEvaluateWithoutResultConversion(expression, documentName); + T result = MapToHostType(resultValue); + + return result; + } + + protected override void InnerExecute(string code) + { + InnerExecute(code, null); + } + + protected override void InnerExecute(string code, string documentName) + { + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + + try + { + lock (_executionSynchronizer) + { + _jsContext.Execute(code, uniqueDocumentName); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + catch (Exception e) when ((e is TargetInvocationException || e is WrapperException) + && e.InnerException != null) + { + OriginalException originalException = OriginalException.From(e.InnerException); + throw originalException; + } + } + + protected override void InnerExecute(IPrecompiledScript precompiledScript) + { + throw new NotSupportedException(); + } + + protected override object InnerCallFunction(string functionName, params object[] args) + { + OriginalValue resultValue = InnerCallFunctionWithoutResultConversion(functionName, args); + object result = MapToHostType(resultValue); + + return result; + } + + protected override T InnerCallFunction(string functionName, params object[] args) + { + OriginalValue resultValue = InnerCallFunctionWithoutResultConversion(functionName, args); + T result = MapToHostType(resultValue); + + return result; + } + + protected override bool InnerHasVariable(string variableName) + { + bool result; + + try + { + OriginalValue variableValue; + + lock (_executionSynchronizer) + { + variableValue = _jsContext[variableName]; + } + + result = !variableValue.IsUndefined; + } + catch (OriginalException) + { + result = false; + } + + return result; + } + + protected override object InnerGetVariableValue(string variableName) + { + OriginalValue variableValue = InnerGetVariableValueWithoutResultConversion(variableName); + object result = MapToHostType(variableValue); + + return result; + } + + protected override T InnerGetVariableValue(string variableName) + { + OriginalValue variableValue = InnerGetVariableValueWithoutResultConversion(variableName); + T result = MapToHostType(variableValue); + + return result; + } + + protected override void InnerSetVariableValue(string variableName, object value) + { + OriginalValue processedValue = MapToScriptType(value); + + try + { + lock (_executionSynchronizer) + { + _jsContext[variableName] = processedValue; + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + + protected override void InnerRemoveVariable(string variableName) + { + try + { + lock (_executionSynchronizer) + { + _jsContext.Delete(variableName); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + + protected override void InnerEmbedHostObject(string itemName, object value) + { + OriginalValue processedValue; + if (value is Delegate) + { + processedValue = CreateEmbeddedFunction((Delegate)value); + } + else + { + processedValue = OriginalClrProxy.From(value); + } + + try + { + lock (_executionSynchronizer) + { + _jsContext[itemName] = processedValue; + + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + + protected override void InnerEmbedHostType(string itemName, Type type) + { + OriginalValue processedValue = OriginalClrType.From(type); + + try + { + lock (_executionSynchronizer) + { + _jsContext[itemName] = processedValue; + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } + + protected override void InnerInterrupt() + { + throw new NotSupportedException(); + } + + protected override void InnerCollectGarbage() + { + throw new NotSupportedException(); + } + + #region IJsEngine implementation + + public override string Name + { + get { return EngineName; } + } + + public override string Version + { + get { return EngineVersion; } + } + + public override bool SupportsScriptPrecompilation + { + get { return false; } + } + + public override bool SupportsScriptInterruption + { + get { return false; } + } + + public override bool SupportsGarbageCollection + { + get { return false; } + } + + #endregion + + #region IDisposable implementation + + public override void Dispose() + { + if (_disposedFlag.Set()) + { + lock (_executionSynchronizer) + { + if (_jsContext != null) + { + if (_consoleCallback != null) + { + _jsContext.ConsoleEvent -= OnConsoleWrite; + _consoleCallback = null; + } + + _jsContext.Dispose(); + _jsContext = null; + } + } + } + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Yantra/YantraSettings.cs b/src/JavaScriptEngineSwitcher.Yantra/YantraSettings.cs new file mode 100644 index 00000000..0383c1fe --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Yantra/YantraSettings.cs @@ -0,0 +1,39 @@ +using OriginalDebugger = YantraJS.Debugger.JSDebugger; +using OriginalV8Debugger = YantraJS.Core.Debugger.V8Debugger; + +namespace JavaScriptEngineSwitcher.Yantra +{ + /// + /// Settings of the Yantra JS engine + /// + public sealed class YantraSettings + { + /// + /// Gets or sets a JS debugging console callback + /// + public YantraJsConsoleCallback ConsoleCallback + { + get; + set; + } + + /// + /// Gets or sets an instance of JS debugger (for example, the ) + /// + public OriginalDebugger Debugger + { + get; + set; + } + + + /// + /// Constructs an instance of the Yantra settings + /// + public YantraSettings() + { + ConsoleCallback = null; + Debugger = null; + } + } +} \ No newline at end of file diff --git a/src/JavaScriptEngineSwitcher.Yantra/readme.txt b/src/JavaScriptEngineSwitcher.Yantra/readme.txt new file mode 100644 index 00000000..78ad2ea3 --- /dev/null +++ b/src/JavaScriptEngineSwitcher.Yantra/readme.txt @@ -0,0 +1,26 @@ + + + -------------------------------------------------------------------------------- + README file for JS Engine Switcher: Yantra v3.30.2 + + -------------------------------------------------------------------------------- + + Copyright (c) 2013-2025 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + JavaScriptEngineSwitcher.Yantra contains a `YantraJsEngine` adapter (wrapper for the + YantraJS (https://github.com/yantrajs/yantra) version 1.2.293). + + ============= + RELEASE NOTES + ============= + YantraJS was updated to version 1.2.293. + + ============= + DOCUMENTATION + ============= + See documentation on GitHub - + http://github.com/Taritsyn/JavaScriptEngineSwitcher \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Assert.cs b/test/JavaScriptEngineSwitcher.Benchmarks/Assert.cs new file mode 100644 index 00000000..2ccd4123 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Assert.cs @@ -0,0 +1,104 @@ +using System; +using System.Runtime.CompilerServices; +using System.Text; + +namespace JavaScriptEngineSwitcher.Benchmarks +{ + internal static class Assert + { + private static readonly char[] _lineBreakChars = new[] { '\r', '\n' }; + + + public static void Equal(string expected, string actual, bool ignoreLineBreaks = false) + { + if (!EqualInternal(expected, actual, ignoreLineBreaks)) + { + var messageBuilder = new StringBuilder(); + messageBuilder.AppendLine("Assert.Equal() Failure"); + messageBuilder.AppendLine(); + messageBuilder.AppendLine($"Expected: {expected}"); + messageBuilder.Append($"Actual: {actual}"); + + string errorMessage = messageBuilder.ToString(); + + throw new InvalidOperationException(errorMessage); + } + } + + private static bool EqualInternal(string a, string b, bool ignoreLineBreaks) + { + if (!ignoreLineBreaks) + { + return a == b; + } + + if (ReferenceEquals(a, b)) + { + return true; + } + + if (a == null || b == null) + { + return false; + } + + if (a.IndexOfAny(_lineBreakChars) == -1 && b.IndexOfAny(_lineBreakChars) == -1) + { + return a.Equals(b); + } + + int aIndex = 0; + int aLength = a.Length; + int bIndex = 0; + int bLength = b.Length; + + while (true) + { + if (aIndex >= aLength) + { + return bIndex >= bLength; + } + + if (bIndex >= bLength) + { + return false; + } + + char aChar = a[aIndex]; + char bChar = b[bIndex]; + + if (aChar != bChar) + { + if (Array.IndexOf(_lineBreakChars, aChar) != -1 && Array.IndexOf(_lineBreakChars, bChar) != -1) + { + ProcessLineBreaks(a, aChar, ref aIndex, aLength); + ProcessLineBreaks(b, bChar, ref bIndex, bLength); + + continue; + } + else + { + return false; + } + } + + aIndex++; + bIndex++; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ProcessLineBreaks(string value, char charValue, ref int charIndex, int charCount) + { + if (charValue == '\r') + { + int nextCharIndex = charIndex + 1; + if (nextCharIndex < charCount && value[nextCharIndex] == '\n') + { + charIndex++; + } + } + charIndex++; + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/contacts/data.json b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/contacts/data.json new file mode 100644 index 00000000..04b3a6c0 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/contacts/data.json @@ -0,0 +1,20 @@ +{ + "lastName": "Пупкин", + "firstName": "Василий", + "patronymic": "Иванович", + "address": { + "postalCode": 392032, + "country": "Россия", + "state": "Тамбовская область", + "city": "Тамбов", + "street": "Магистральная", + "houseNumber": "41к7", + "floor": 8, + "apartmentNumber": 115 + }, + "homePhone": "+7 (4752) 555-55-55", + "mobilePhone": "+7 (999) 689-99-99", + "email": "pupkin@mail.ru", + "website": "http:\/\/pupkin.narod.ru", + "icq": 698426795 +} diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/contacts/target-output.html b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/contacts/target-output.html new file mode 100644 index 00000000..0c69826e --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/contacts/target-output.html @@ -0,0 +1,11 @@ +
+ Ф.И.О.: Пупкин Василий Иванович
+ Адрес: + 392032, Россия, Тамбовская область, Тамбов, ул. Магистральная, д. 41к7, кв. 115 +
+ Дом. тел.: +7 (4752) 555-55-55
+ Моб. тел.: +7 (999) 689-99-99
+ Email: pupkin@mail.ru
+ Сайт: http://pupkin.narod.ru
+ ICQ: ICQ статус698426795 +
\ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/contacts/template.handlebars b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/contacts/template.handlebars new file mode 100644 index 00000000..99acf502 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/contacts/template.handlebars @@ -0,0 +1,13 @@ +
+ Ф.И.О.: {{lastName}} {{firstName}} {{patronymic}}
+ Адрес: + {{#with address}} + {{postalCode}}, {{country}}, {{state}}, {{city}}, ул. {{street}}, д. {{houseNumber}}, кв. {{apartmentNumber}} + {{/with}} +
+ Дом. тел.: {{homePhone}}
+ Моб. тел.: {{mobilePhone}}
+ Email: {{email}}
+ Сайт: {{{link website website false}}}
+ ICQ: ICQ статус{{icq}} +
\ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/hello-world/data.json b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/hello-world/data.json new file mode 100644 index 00000000..cecaa5d7 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/hello-world/data.json @@ -0,0 +1,6 @@ +{ + "lastName": "Pupkin", + "firstName": "Vasilii", + "patronymic": "Ivanovich", + "occupation": "Enikeyschik" +} diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/hello-world/target-output.html b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/hello-world/target-output.html new file mode 100644 index 00000000..6ae42cce --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/hello-world/target-output.html @@ -0,0 +1,3 @@ +
+ Hello, Vasilii Ivanovich! +
\ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/hello-world/template.handlebars b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/hello-world/template.handlebars new file mode 100644 index 00000000..c1a4dc47 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/hello-world/template.handlebars @@ -0,0 +1,4 @@ +
+ {{!-- Welcome by first name and patronymic --}} + Hello, {{firstName}} {{patronymic}}! +
\ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/js-engines/data.json b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/js-engines/data.json new file mode 100644 index 00000000..0d76d661 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/js-engines/data.json @@ -0,0 +1,235 @@ +{ + "categories": [ + { + "name": "Actual", + "engines": [ + { + "id": "besen", + "name": "BESEN", + "url": "https:\/\/github.com\/BeRo1985\/besen", + "description": "A acronym for “B<\/strong> ero’s E<\/strong> cma S<\/strong> cript E<\/strong> ngine”, and it is a complete ECMAScript 5 implementation in Object Pascal, which is compilable with Delphi >=7 and Free Pascal >= 2.5.1 (maybe also 2.4.1)." + }, + { + "id": "chakra-edge", + "name": "Chakra “Edge”", + "url": "https:\/\/blogs.windows.com\/msedgedev\/2015\/05\/18\/using-chakra-for-scripting-applications-across-windows-10\/", + "description": "A JavaScript engine developed by Microsoft<\/a> for its Edge web browser. It is a fork of the JScript9<\/a> engine used in Internet Explorer." + }, + { + "id": "chakra-core", + "name": "ChakraCore", + "url": "https:\/\/github.com\/Microsoft\/ChakraCore", + "description": "A core part of Chakra, the high-performance JavaScript engine that powers Microsoft Edge and Windows applications written in HTML\/CSS\/JS. ChakraCore supports Just-in-time (JIT) compilation of JavaScript for x86\/x64\/ARM, garbage collection, and a wide range of the latest JavaScript features. ChakraCore also supports the JavaScript Runtime (JSRT) APIs<\/a>, which allows you to easily embed ChakraCore in your applications." + }, + { + "id": "duktape", + "name": "Duktape", + "url": "https:\/\/duktape.org\/", + "description": "A embeddable JavaScript engine, with a focus on portability and compact footprint." + }, + { + "id": "espruino", + "name": "Espruino", + "url": "https:\/\/github.com\/espruino\/Espruino", + "description": "A JavaScript interpreter for microcontrollers. It designed for devices with as little as 128 KB Flash and 8 KB RAM." + }, + { + "id": "hermes", + "name": "Hermes", + "url": "https:\/\/github.com\/facebook\/hermes", + "description": "A Facebook<\/a>’s JavaScript engine optimized for fast start up of React Native<\/a> apps on Android. It features ahead-of-time static optimization and compact bytecode." + }, + { + "id": "javascript-core", + "name": "JavaScriptCore", + "url": "https:\/\/developer.apple.com\/documentation\/javascriptcore", + "description": "A Apple<\/a>’s Framework provides the ability to evaluate JavaScript programs from within Swift, Objective-C, and C-based apps. You can use also use JavaScriptCore to insert custom objects to the JavaScript environment." + }, + { + "id": "jerry-script", + "name": "JerryScript", + "url": "https:\/\/jerryscript.net\/", + "description": "A lightweight JavaScript engine for resource-constrained devices such as microcontrollers. It can run on devices with less than 64 KB of RAM and less than 200 KB of flash memory." + }, + { + "id": "jint", + "name": "Jint", + "url": "https:\/\/github.com\/sebastienros\/jint", + "description": "A JavaScript interpreter for .NET which provides full ECMAScript 5.1 compliance and can run on any .NET platform. Because it doesn't generate any .NET bytecode nor use the DLR it runs relatively small scripts faster." + }, + { + "id": "js-interpreter", + "name": "JS-Interpreter", + "url": "https:\/\/github.com\/NeilFraser\/JS-Interpreter", + "description": "A sandboxed JavaScript interpreter in JavaScript. Execute arbitrary JavaScript code line by line in isolation and safety." + }, + { + "id": "jsish", + "name": "jsish", + "url": "https:\/\/jsish.org", + "description": "A Javascript interpreter + embedded Web-server." + }, + { + "id": "jurassic", + "name": "Jurassic", + "url": "https:\/\/github.com\/paulbartrum\/jurassic", + "description": "An implementation of the ECMAScript language and runtime. It aims to provide the best performing and most standards-compliant implementation of JavaScript for .NET. Jurassic is not intended for end-users; instead it is intended to be integrated into .NET programs." + }, + { + "id": "kjs", + "name": "KJS", + "url": "https:\/\/api.kde.org\/4.x-api\/kdelibs-apidocs\/kjs\/html\/", + "description": "A KDE<\/a>’s JavaScript engine that was originally developed for the Konqueror<\/a> web browser by Harri Porten in 2000." + }, + { + "id": "mjs", + "name": "mJS", + "url": "https:\/\/github.com\/cesanta\/mjs", + "description": "A restricted JavaScript engine. It designed for microcontrollers with limited resources. Main design goals are small footprint and simple C\/C++ interoperability." + }, + { + "id": "mu-js", + "name": "MuJS", + "url": "https:\/\/mujs.com\/", + "description": "A lightweight JavaScript interpreter designed for embedding in other software to extend them with scripting capabilities. It written in portable C and implements ECMAScript." + }, + { + "id": "nil-js", + "name": "NiL.JS", + "url": "https:\/\/github.com\/nilproject\/NiL.JS", + "description": "A JavaScript engine for .NET written in C# and implements ECMAScript 6 (ES2015)." + }, + { + "id": "quick-js", + "name": "QuickJS", + "url": "https:\/\/bellard.org\/quickjs\/", + "description": "A small and embeddable JavaScript engine. It supports the ES2020 specification including modules, asynchronous generators and proxies." + }, + { + "id": "rhino", + "name": "Rhino", + "url": "https:\/\/github.com\/mozilla\/rhino", + "description": "A JavaScript engine written fully in Java and managed by the Mozilla Foundation<\/a> as open source software." + }, + { + "id": "spider-monkey", + "name": "SpiderMonkey", + "url": "https:\/\/developer.mozilla.org\/en-US\/docs\/Mozilla\/Projects\/SpiderMonkey", + "description": "A Mozilla<\/a>’s JavaScript engine written in C and C++. It used in various Mozilla products, including Firefox, and is available under the MPL2." + }, + { + "id": "tiny-js", + "name": "TinyJS", + "url": "https:\/\/github.com\/gfwilliams\/tiny-js", + "description": "An extremely simple (~2000 line) JavaScript interpreter, meant for inclusion in applications that require a simple, familiar script language that can be included with no dependencies other than normal C++ libraries. It currently consists of two source files: one containing the interpreter, another containing built-in functions such as String.substring<\/code>." + }, + { + "id": "v8", + "name": "V8", + "url": "https:\/\/v8.dev\/", + "description": "A Google<\/a>’s open source high-performance JavaScript and WebAssembly engine, written in C++. It used in Chrome and in Node.js, among others. It implements ECMAScript and WebAssembly, and runs on Windows 7 or later, macOS 10.12+, and Linux systems that use x64, IA-32, ARM, or MIPS processors. V8 can run standalone, or can be embedded into any C++ application." + }, + { + "id": "xs", + "name": "XS", + "url": "https:\/\/github.com\/Moddable-OpenSource\/moddable", + "description": "A JavaScript engine at the center of the Moddable SDK<\/a> (successor of Kinoma<\/a> Platform) implements the 2018 JavaScript language standard with better than 99% conformance. The constraints of the target microcontroller may limit the number of language features that can be used in combination by a single application." + } + ] + }, + { + "name": "Obsolete", + "engines": [ + { + "id": "carakan", + "name": "Carakan", + "url": "https:\/\/dev.opera.com\/blog\/carakan\/", + "description": "A JavaScript engine developed by Opera Software<\/a>, included in the 10.50 release of the Opera web browser, until switching to V8<\/a> with Opera 15." + }, + { + "id": "chakra-ie", + "name": "Chakra “IE” (JScript9)", + "url": "https:\/\/blogs.msdn.microsoft.com\/ie\/2010\/03\/18\/the-new-javascript-engine-in-internet-explorer-9\/", + "description": "A Active Scripting JavaScript engine developed by Microsoft<\/a> for Internet Explorer 9 web browser. Starting in Internet Explorer 11, Chakra added support for a new set of public hosting APIs called the JavaScript Runtime (JSRT) APIs<\/a>, which shipped as part of the Windows SDK." + }, + { + "id": "continuum", + "name": "Continuum", + "url": "https:\/\/github.com\/joskid\/continuum", + "description": "A JavaScript virtual machine built in JavaScript. It assembles bytecode from source code and executes it an ECMAScript 6 runtime environment. The code of the VM is written in ECMAScript 3 level JavaScript, which means it can run in browsers as old as Internet Explorer 6." + }, + { + "id": "iron-js", + "name": "IronJS", + "url": "https:\/\/github.com\/fholm\/IronJS", + "description": "An ECMAScript 3 implementation built on top of the Dynamic Language Runtime from Microsoft<\/a> which allows you to embed a javascript runtime into your .NET applications." + }, + { + "id": "iv", + "name": "iv", + "url": "https:\/\/github.com\/Constellation\/iv", + "description": "A ECMAScript 5.1 lexer and parser and engine project written in C++\/JS." + }, + { + "id": "jscript", + "name": "JScript", + "url": "https:\/\/docs.microsoft.com\/en-us\/previous-versions\/hbxc2t98(v=vs.85)", + "description": "A Microsoft<\/a> implementation of the ECMAScript 3 language specification. It implemented as an Active Scripting<\/a> engine. This means that it can be “plugged in” to OLE Automation applications that support Active Scripting, such as Internet Explorer, Active Server Pages<\/a>, and Windows Script Host<\/a>." + }, + { + "id": "narcissus", + "name": "Narcissus", + "url": "https:\/\/github.com\/mozilla\/narcissus\/", + "description": "A JavaScript interpreter written in pure JavaScript (i.e., a meta-circular evaluator<\/a>), using the SpiderMonkey<\/a> engine. Originally a proof-of-concept by Brendan Eich<\/a>, Narcissus is being revived as a test-bed for rapidly prototyping new language features for the JavaScript language (as well as the ECMAScript standard)." + }, + { + "id": "nashorn", + "name": "Nashorn", + "url": "https:\/\/blogs.oracle.com\/nashorn\/", + "description": "A JavaScript engine developed in the Java programming language by Oracle<\/a>. It based on the Da Vinci Machine<\/a> (JSR 292) and has been released with Java 8." + }, + { + "id": "v7", + "name": "V7", + "url": "https:\/\/github.com\/cesanta\/v7\/", + "description": "A smallest JavaScript engine written in C. This project is deprecated in favor of the mJS<\/a>." + } + ] + }, + { + "name": "Supersets", + "engines": [ + { + "id": "cl-javascript", + "name": "CL-JavaScript", + "url": "http:\/\/marijnhaverbeke.nl\/cl-javascript\/", + "description": "Allows you to add user scripting to your Common Lisp<\/a> application without requiring your poor users to learn Common Lisp. It is a JavaScript to Common Lisp translator, runtime, and standard library. CL-JavaScript are ECMAScript 3 compatible, with some of the ECMAScript 5 extensions." + }, + { + "id": "in-script", + "name": "InScript", + "url": "https:\/\/www.muchsoft.com\/inscript\/", + "description": "A scripting language used by iCab<\/a> to generate HTML documents dynamically. The core language is compatible with ECMAScript. At present InScript features roughly the functionality of JavaScript 1.5." + }, + { + "id": "njs", + "name": "njs", + "url": "http:\/\/nginx.org\/en\/docs\/njs\/", + "description": "A subset of the JavaScript language that allows extending nginx<\/a> functionality. It created in compliance with ECMAScript 5.1 (strict mode) with some ECMAScript 6 and later extensions. The compliance is still evolving." + }, + { + "id": "qt-script", + "name": "Qt Script", + "url": "https:\/\/doc.qt.io\/QT-5\/qtscript-index.html", + "description": "A scripting engine that has been part of the Qt<\/a> cross-platform application framework since version 4.3.0." + }, + { + "id": "tamarin", + "name": "Tamarin", + "url": "https:\/\/developer.mozilla.org\/en-US\/docs\/Archive\/Mozilla\/Tamarin", + "description": "A JavaScript engine written in C++. It currently implements Adobe ActionScript 3<\/a> (a superset of ECMAScript 3) and is embedded within the Adobe Flash Player<\/a> versions 9 and later. Tamarin’s JIT-compiler, NanoJIT<\/a>, is also used in TraceMonkey<\/a> ergo SpiderMonkey<\/a>." + } + ] + } + ] +} diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/js-engines/target-output.html b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/js-engines/target-output.html new file mode 100644 index 00000000..7482a263 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/js-engines/target-output.html @@ -0,0 +1,46 @@ +

List of JavaScript engines

+

Actual

+
    +
  1. BESEN — A acronym for “B ero’s E cma S cript E ngine”, and it is a complete ECMAScript 5 implementation in Object Pascal, which is compilable with Delphi >=7 and Free Pascal >= 2.5.1 (maybe also 2.4.1).
  2. +
  3. Chakra “Edge” — A JavaScript engine developed by Microsoft for its Edge web browser. It is a fork of the JScript9 engine used in Internet Explorer.
  4. +
  5. ChakraCore — A core part of Chakra, the high-performance JavaScript engine that powers Microsoft Edge and Windows applications written in HTML/CSS/JS. ChakraCore supports Just-in-time (JIT) compilation of JavaScript for x86/x64/ARM, garbage collection, and a wide range of the latest JavaScript features. ChakraCore also supports the JavaScript Runtime (JSRT) APIs, which allows you to easily embed ChakraCore in your applications.
  6. +
  7. Duktape — A embeddable JavaScript engine, with a focus on portability and compact footprint.
  8. +
  9. Espruino — A JavaScript interpreter for microcontrollers. It designed for devices with as little as 128 KB Flash and 8 KB RAM.
  10. +
  11. Hermes — A Facebook’s JavaScript engine optimized for fast start up of React Native apps on Android. It features ahead-of-time static optimization and compact bytecode.
  12. +
  13. JavaScriptCore — A Apple’s Framework provides the ability to evaluate JavaScript programs from within Swift, Objective-C, and C-based apps. You can use also use JavaScriptCore to insert custom objects to the JavaScript environment.
  14. +
  15. JerryScript — A lightweight JavaScript engine for resource-constrained devices such as microcontrollers. It can run on devices with less than 64 KB of RAM and less than 200 KB of flash memory.
  16. +
  17. Jint — A JavaScript interpreter for .NET which provides full ECMAScript 5.1 compliance and can run on any .NET platform. Because it doesn't generate any .NET bytecode nor use the DLR it runs relatively small scripts faster.
  18. +
  19. JS-Interpreter — A sandboxed JavaScript interpreter in JavaScript. Execute arbitrary JavaScript code line by line in isolation and safety.
  20. +
  21. jsish — A Javascript interpreter + embedded Web-server.
  22. +
  23. Jurassic — An implementation of the ECMAScript language and runtime. It aims to provide the best performing and most standards-compliant implementation of JavaScript for .NET. Jurassic is not intended for end-users; instead it is intended to be integrated into .NET programs.
  24. +
  25. KJS — A KDE’s JavaScript engine that was originally developed for the Konqueror web browser by Harri Porten in 2000.
  26. +
  27. mJS — A restricted JavaScript engine. It designed for microcontrollers with limited resources. Main design goals are small footprint and simple C/C++ interoperability.
  28. +
  29. MuJS — A lightweight JavaScript interpreter designed for embedding in other software to extend them with scripting capabilities. It written in portable C and implements ECMAScript.
  30. +
  31. NiL.JS — A JavaScript engine for .NET written in C# and implements ECMAScript 6 (ES2015).
  32. +
  33. QuickJS — A small and embeddable JavaScript engine. It supports the ES2020 specification including modules, asynchronous generators and proxies.
  34. +
  35. Rhino — A JavaScript engine written fully in Java and managed by the Mozilla Foundation as open source software.
  36. +
  37. SpiderMonkey — A Mozilla’s JavaScript engine written in C and C++. It used in various Mozilla products, including Firefox, and is available under the MPL2.
  38. +
  39. TinyJS — An extremely simple (~2000 line) JavaScript interpreter, meant for inclusion in applications that require a simple, familiar script language that can be included with no dependencies other than normal C++ libraries. It currently consists of two source files: one containing the interpreter, another containing built-in functions such as String.substring.
  40. +
  41. V8 — A Google’s open source high-performance JavaScript and WebAssembly engine, written in C++. It used in Chrome and in Node.js, among others. It implements ECMAScript and WebAssembly, and runs on Windows 7 or later, macOS 10.12+, and Linux systems that use x64, IA-32, ARM, or MIPS processors. V8 can run standalone, or can be embedded into any C++ application.
  42. +
  43. XS — A JavaScript engine at the center of the Moddable SDK (successor of Kinoma Platform) implements the 2018 JavaScript language standard with better than 99% conformance. The constraints of the target microcontroller may limit the number of language features that can be used in combination by a single application.
  44. +
+

Obsolete

+
    +
  1. Carakan — A JavaScript engine developed by Opera Software, included in the 10.50 release of the Opera web browser, until switching to V8 with Opera 15.
  2. +
  3. Chakra “IE” (JScript9) — A Active Scripting JavaScript engine developed by Microsoft for Internet Explorer 9 web browser. Starting in Internet Explorer 11, Chakra added support for a new set of public hosting APIs called the JavaScript Runtime (JSRT) APIs, which shipped as part of the Windows SDK.
  4. +
  5. Continuum — A JavaScript virtual machine built in JavaScript. It assembles bytecode from source code and executes it an ECMAScript 6 runtime environment. The code of the VM is written in ECMAScript 3 level JavaScript, which means it can run in browsers as old as Internet Explorer 6.
  6. +
  7. IronJS — An ECMAScript 3 implementation built on top of the Dynamic Language Runtime from Microsoft which allows you to embed a javascript runtime into your .NET applications.
  8. +
  9. iv — A ECMAScript 5.1 lexer and parser and engine project written in C++/JS.
  10. +
  11. JScript — A Microsoft implementation of the ECMAScript 3 language specification. It implemented as an Active Scripting engine. This means that it can be “plugged in” to OLE Automation applications that support Active Scripting, such as Internet Explorer, Active Server Pages, and Windows Script Host.
  12. +
  13. Narcissus — A JavaScript interpreter written in pure JavaScript (i.e., a meta-circular evaluator), using the SpiderMonkey engine. Originally a proof-of-concept by Brendan Eich, Narcissus is being revived as a test-bed for rapidly prototyping new language features for the JavaScript language (as well as the ECMAScript standard).
  14. +
  15. Nashorn — A JavaScript engine developed in the Java programming language by Oracle. It based on the Da Vinci Machine (JSR 292) and has been released with Java 8.
  16. +
  17. V7 — A smallest JavaScript engine written in C. This project is deprecated in favor of the mJS.
  18. +
+

Supersets

+
    +
  1. CL-JavaScript — Allows you to add user scripting to your Common Lisp application without requiring your poor users to learn Common Lisp. It is a JavaScript to Common Lisp translator, runtime, and standard library. CL-JavaScript are ECMAScript 3 compatible, with some of the ECMAScript 5 extensions.
  2. +
  3. InScript — A scripting language used by iCab to generate HTML documents dynamically. The core language is compatible with ECMAScript. At present InScript features roughly the functionality of JavaScript 1.5.
  4. +
  5. njs — A subset of the JavaScript language that allows extending nginx functionality. It created in compliance with ECMAScript 5.1 (strict mode) with some ECMAScript 6 and later extensions. The compliance is still evolving.
  6. +
  7. Qt Script — A scripting engine that has been part of the Qt cross-platform application framework since version 4.3.0.
  8. +
  9. Tamarin — A JavaScript engine written in C++. It currently implements Adobe ActionScript 3 (a superset of ECMAScript 3) and is embedded within the Adobe Flash Player versions 9 and later. Tamarin’s JIT-compiler, NanoJIT, is also used in TraceMonkey ergo SpiderMonkey.
  10. +
diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/js-engines/template.handlebars b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/js-engines/template.handlebars new file mode 100644 index 00000000..e6ee8a09 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/js-engines/template.handlebars @@ -0,0 +1,9 @@ +

List of JavaScript engines

+{{#each categories}} +

{{name}}

+
    +{{#each engines}} +
  1. {{{link name url true}}} — {{{description}}}
  2. +{{/each}} +
+{{/each}} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/web-browser-family-tree/data.json b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/web-browser-family-tree/data.json new file mode 100644 index 00000000..4a355b73 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/web-browser-family-tree/data.json @@ -0,0 +1,98 @@ +[ + { "name": "WorldWideWeb" }, + { "name": "ViolaWWW" }, + { "name": "Erwise" }, + { "name": "Lynx" }, + { "name": "TkWWW" }, + { "name": "MidasWWW" }, + { "name": "NCSA Mosaic", "nodes": [ + { "name": "Netscape Navigator 1.0-4.8", "nodes": [ + { "name": "Mozilla Suite", "nodes": [ + { "name": "Netscape Navigator 6.0-7.2" }, + { "name": "Galeon", "nodes": [ + { "name": "Epiphany <2.28" } + ]}, + { "name": "K-Meleon" }, + { "name": "Phoenix", "nodes": [ + { "name": "Mozilla Firebird", "nodes": [ + { "name": "Mozilla Firefox", "nodes": [ + { "name": "Netscape Browser", "nodes": [ + { "name": "Netscape Navigator 9.0" } + ]}, + { "name": "Flock <=2.6.2" }, + { "name": "GNUzilla IceWeasel", "nodes": [ + { "name": "GNUzilla IceCat" } + ]}, + { "name": "Debian Firefox", "nodes": [ + { "name": "Debian Iceweasel" } + ]}, + { "name": "Pale Moon" }, + { "name": "Avant Browser >=2012" } + ]} + ]} + ]}, + { "name": "Lunascape" }, + { "name": "Camino" }, + { "name": "SeaMonkey" } + ]} + ]}, + { "name": "Air Mosaic" }, + { "name": "Infomosaic" }, + { "name": "Spyglass Mosaic", "nodes": [ + { "name": "Internet Explorer", "nodes": [ + { "name": "MyIE", "nodes": [ + { "name": "Maxthon" } + ]}, + { "name": "Lunascape" }, + { "name": "Avant Browser" }, + { "name": "Netscape Browser" }, + { "name": "Sogou Browser" }, + { "name": "QQ browser" }, + { "name": "Microsoft Edge (Project Spartan)" } + ]} + ]} + ]}, + { "name": "Cello" }, + { "name": "OmniWeb <4.5" }, + { "name": "Opera <15" }, + { "name": "Amaya" }, + { "name": "Konqueror" , "nodes": [ + { "name": "Safari", "nodes": [ + { "name": "OmniWeb >=4.5" }, + { "name": "Midori" }, + { "name": "Epiphany >=2.28" }, + { "name": "Google Chrome", "nodes": [ + { "name": "Chromium", "nodes": [ + { "name": "Lunascape" }, + { "name": "SRWare Iron" }, + { "name": "360 Secure Browser" }, + { "name": "Sogou Browser" }, + { "name": "CoolNovo" }, + { "name": "Maxthon >=3" }, + { "name": "Flock >=3" }, + { "name": "RockMelt" }, + { "name": "Avant Browser >=2012" }, + { "name": "Яндекс.Браузер" }, + { "name": "QQ browser" }, + { "name": "Opera >=15" }, + { "name": "Амиго" }, + { "name": "Cốc Cốc" }, + { "name": "Vivaldi" }, + { "name": "Brave" }, + { "name": "Naver Whale" }, + { "name": "Beaker" }, + { "name": "Microsoft Edge (Anaheim)" } + ]} + ]}, + { "name": "Arora" } + ]} + ]}, + { "name": "Arachne" }, + { "name": "Links", "nodes": [ + { "name": "ELinks" }, + { "name": "Hacked Links" } + ]}, + { "name": "iCab" }, + { "name": "Dillo" }, + { "name": "UC Browser" } +] diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/web-browser-family-tree/target-output.html b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/web-browser-family-tree/target-output.html new file mode 100644 index 00000000..3142039b Binary files /dev/null and b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/web-browser-family-tree/target-output.html differ diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/web-browser-family-tree/template.handlebars b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/web-browser-family-tree/template.handlebars new file mode 100644 index 00000000..3fccb266 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/content/web-browser-family-tree/template.handlebars @@ -0,0 +1,16 @@ +{{#* inline "treeNodes"}} +{{#each nodes}} +
  • + {{name}} + {{#if nodes}} +
      + {{> treeNodes}} +
    + {{/if}} +
  • +{{/each}} +{{/inline}} +

    Family tree of web browsers

    +
      +{{> treeNodes nodes=this}} +
    \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/lib/handlebars.js b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/lib/handlebars.js new file mode 100644 index 00000000..baad5d3d --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/lib/handlebars.js @@ -0,0 +1,5210 @@ +/**! + + @license + handlebars v4.7.7 + +Copyright (C) 2011-2019 by Yehuda Katz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["Handlebars"] = factory(); + else + root["Handlebars"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _handlebarsRuntime = __webpack_require__(2); + + var _handlebarsRuntime2 = _interopRequireDefault(_handlebarsRuntime); + + // Compiler imports + + var _handlebarsCompilerAst = __webpack_require__(45); + + var _handlebarsCompilerAst2 = _interopRequireDefault(_handlebarsCompilerAst); + + var _handlebarsCompilerBase = __webpack_require__(46); + + var _handlebarsCompilerCompiler = __webpack_require__(51); + + var _handlebarsCompilerJavascriptCompiler = __webpack_require__(52); + + var _handlebarsCompilerJavascriptCompiler2 = _interopRequireDefault(_handlebarsCompilerJavascriptCompiler); + + var _handlebarsCompilerVisitor = __webpack_require__(49); + + var _handlebarsCompilerVisitor2 = _interopRequireDefault(_handlebarsCompilerVisitor); + + var _handlebarsNoConflict = __webpack_require__(44); + + var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict); + + var _create = _handlebarsRuntime2['default'].create; + function create() { + var hb = _create(); + + hb.compile = function (input, options) { + return _handlebarsCompilerCompiler.compile(input, options, hb); + }; + hb.precompile = function (input, options) { + return _handlebarsCompilerCompiler.precompile(input, options, hb); + }; + + hb.AST = _handlebarsCompilerAst2['default']; + hb.Compiler = _handlebarsCompilerCompiler.Compiler; + hb.JavaScriptCompiler = _handlebarsCompilerJavascriptCompiler2['default']; + hb.Parser = _handlebarsCompilerBase.parser; + hb.parse = _handlebarsCompilerBase.parse; + hb.parseWithoutProcessing = _handlebarsCompilerBase.parseWithoutProcessing; + + return hb; + } + + var inst = create(); + inst.create = create; + + _handlebarsNoConflict2['default'](inst); + + inst.Visitor = _handlebarsCompilerVisitor2['default']; + + inst['default'] = inst; + + exports['default'] = inst; + module.exports = exports['default']; + +/***/ }), +/* 1 */ +/***/ (function(module, exports) { + + "use strict"; + + exports["default"] = function (obj) { + return obj && obj.__esModule ? obj : { + "default": obj + }; + }; + + exports.__esModule = true; + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _handlebarsBase = __webpack_require__(4); + + var base = _interopRequireWildcard(_handlebarsBase); + + // Each of these augment the Handlebars object. No need to setup here. + // (This is done to easily share code between commonjs and browse envs) + + var _handlebarsSafeString = __webpack_require__(37); + + var _handlebarsSafeString2 = _interopRequireDefault(_handlebarsSafeString); + + var _handlebarsException = __webpack_require__(6); + + var _handlebarsException2 = _interopRequireDefault(_handlebarsException); + + var _handlebarsUtils = __webpack_require__(5); + + var Utils = _interopRequireWildcard(_handlebarsUtils); + + var _handlebarsRuntime = __webpack_require__(38); + + var runtime = _interopRequireWildcard(_handlebarsRuntime); + + var _handlebarsNoConflict = __webpack_require__(44); + + var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict); + + // For compatibility and usage outside of module systems, make the Handlebars object a namespace + function create() { + var hb = new base.HandlebarsEnvironment(); + + Utils.extend(hb, base); + hb.SafeString = _handlebarsSafeString2['default']; + hb.Exception = _handlebarsException2['default']; + hb.Utils = Utils; + hb.escapeExpression = Utils.escapeExpression; + + hb.VM = runtime; + hb.template = function (spec) { + return runtime.template(spec, hb); + }; + + return hb; + } + + var inst = create(); + inst.create = create; + + _handlebarsNoConflict2['default'](inst); + + inst['default'] = inst; + + exports['default'] = inst; + module.exports = exports['default']; + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + + "use strict"; + + exports["default"] = function (obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {}; + + if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; + } + } + + newObj["default"] = obj; + return newObj; + } + }; + + exports.__esModule = true; + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.HandlebarsEnvironment = HandlebarsEnvironment; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _helpers = __webpack_require__(10); + + var _decorators = __webpack_require__(30); + + var _logger = __webpack_require__(32); + + var _logger2 = _interopRequireDefault(_logger); + + var _internalProtoAccess = __webpack_require__(33); + + var VERSION = '4.7.7'; + exports.VERSION = VERSION; + var COMPILER_REVISION = 8; + exports.COMPILER_REVISION = COMPILER_REVISION; + var LAST_COMPATIBLE_COMPILER_REVISION = 7; + + exports.LAST_COMPATIBLE_COMPILER_REVISION = LAST_COMPATIBLE_COMPILER_REVISION; + var REVISION_CHANGES = { + 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it + 2: '== 1.0.0-rc.3', + 3: '== 1.0.0-rc.4', + 4: '== 1.x.x', + 5: '== 2.0.0-alpha.x', + 6: '>= 2.0.0-beta.1', + 7: '>= 4.0.0 <4.3.0', + 8: '>= 4.3.0' + }; + + exports.REVISION_CHANGES = REVISION_CHANGES; + var objectType = '[object Object]'; + + function HandlebarsEnvironment(helpers, partials, decorators) { + this.helpers = helpers || {}; + this.partials = partials || {}; + this.decorators = decorators || {}; + + _helpers.registerDefaultHelpers(this); + _decorators.registerDefaultDecorators(this); + } + + HandlebarsEnvironment.prototype = { + constructor: HandlebarsEnvironment, + + logger: _logger2['default'], + log: _logger2['default'].log, + + registerHelper: function registerHelper(name, fn) { + if (_utils.toString.call(name) === objectType) { + if (fn) { + throw new _exception2['default']('Arg not supported with multiple helpers'); + } + _utils.extend(this.helpers, name); + } else { + this.helpers[name] = fn; + } + }, + unregisterHelper: function unregisterHelper(name) { + delete this.helpers[name]; + }, + + registerPartial: function registerPartial(name, partial) { + if (_utils.toString.call(name) === objectType) { + _utils.extend(this.partials, name); + } else { + if (typeof partial === 'undefined') { + throw new _exception2['default']('Attempting to register a partial called "' + name + '" as undefined'); + } + this.partials[name] = partial; + } + }, + unregisterPartial: function unregisterPartial(name) { + delete this.partials[name]; + }, + + registerDecorator: function registerDecorator(name, fn) { + if (_utils.toString.call(name) === objectType) { + if (fn) { + throw new _exception2['default']('Arg not supported with multiple decorators'); + } + _utils.extend(this.decorators, name); + } else { + this.decorators[name] = fn; + } + }, + unregisterDecorator: function unregisterDecorator(name) { + delete this.decorators[name]; + }, + /** + * Reset the memory of illegal property accesses that have already been logged. + * @deprecated should only be used in handlebars test-cases + */ + resetLoggedPropertyAccesses: function resetLoggedPropertyAccesses() { + _internalProtoAccess.resetLoggedProperties(); + } + }; + + var log = _logger2['default'].log; + + exports.log = log; + exports.createFrame = _utils.createFrame; + exports.logger = _logger2['default']; + +/***/ }), +/* 5 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + exports.extend = extend; + exports.indexOf = indexOf; + exports.escapeExpression = escapeExpression; + exports.isEmpty = isEmpty; + exports.createFrame = createFrame; + exports.blockParams = blockParams; + exports.appendContextPath = appendContextPath; + var escape = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`', + '=': '=' + }; + + var badChars = /[&<>"'`=]/g, + possible = /[&<>"'`=]/; + + function escapeChar(chr) { + return escape[chr]; + } + + function extend(obj /* , ...source */) { + for (var i = 1; i < arguments.length; i++) { + for (var key in arguments[i]) { + if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { + obj[key] = arguments[i][key]; + } + } + } + + return obj; + } + + var toString = Object.prototype.toString; + + exports.toString = toString; + // Sourced from lodash + // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt + /* eslint-disable func-style */ + var isFunction = function isFunction(value) { + return typeof value === 'function'; + }; + // fallback for older versions of Chrome and Safari + /* istanbul ignore next */ + if (isFunction(/x/)) { + exports.isFunction = isFunction = function (value) { + return typeof value === 'function' && toString.call(value) === '[object Function]'; + }; + } + exports.isFunction = isFunction; + + /* eslint-enable func-style */ + + /* istanbul ignore next */ + var isArray = Array.isArray || function (value) { + return value && typeof value === 'object' ? toString.call(value) === '[object Array]' : false; + }; + + exports.isArray = isArray; + // Older IE versions do not directly support indexOf so we must implement our own, sadly. + + function indexOf(array, value) { + for (var i = 0, len = array.length; i < len; i++) { + if (array[i] === value) { + return i; + } + } + return -1; + } + + function escapeExpression(string) { + if (typeof string !== 'string') { + // don't escape SafeStrings, since they're already safe + if (string && string.toHTML) { + return string.toHTML(); + } else if (string == null) { + return ''; + } else if (!string) { + return string + ''; + } + + // Force a string conversion as this will be done by the append regardless and + // the regex test will do this transparently behind the scenes, causing issues if + // an object's to string has escaped characters in it. + string = '' + string; + } + + if (!possible.test(string)) { + return string; + } + return string.replace(badChars, escapeChar); + } + + function isEmpty(value) { + if (!value && value !== 0) { + return true; + } else if (isArray(value) && value.length === 0) { + return true; + } else { + return false; + } + } + + function createFrame(object) { + var frame = extend({}, object); + frame._parent = object; + return frame; + } + + function blockParams(params, ids) { + params.path = ids; + return params; + } + + function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _Object$defineProperty = __webpack_require__(7)['default']; + + exports.__esModule = true; + var errorProps = ['description', 'fileName', 'lineNumber', 'endLineNumber', 'message', 'name', 'number', 'stack']; + + function Exception(message, node) { + var loc = node && node.loc, + line = undefined, + endLineNumber = undefined, + column = undefined, + endColumn = undefined; + + if (loc) { + line = loc.start.line; + endLineNumber = loc.end.line; + column = loc.start.column; + endColumn = loc.end.column; + + message += ' - ' + line + ':' + column; + } + + var tmp = Error.prototype.constructor.call(this, message); + + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } + + /* istanbul ignore else */ + if (Error.captureStackTrace) { + Error.captureStackTrace(this, Exception); + } + + try { + if (loc) { + this.lineNumber = line; + this.endLineNumber = endLineNumber; + + // Work around issue under safari where we can't directly set the column value + /* istanbul ignore next */ + if (_Object$defineProperty) { + Object.defineProperty(this, 'column', { + value: column, + enumerable: true + }); + Object.defineProperty(this, 'endColumn', { + value: endColumn, + enumerable: true + }); + } else { + this.column = column; + this.endColumn = endColumn; + } + } + } catch (nop) { + /* Ignore if the browser is very particular */ + } + } + + Exception.prototype = new Error(); + + exports['default'] = Exception; + module.exports = exports['default']; + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(8), __esModule: true }; + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + + var $ = __webpack_require__(9); + module.exports = function defineProperty(it, key, desc){ + return $.setDesc(it, key, desc); + }; + +/***/ }), +/* 9 */ +/***/ (function(module, exports) { + + var $Object = Object; + module.exports = { + create: $Object.create, + getProto: $Object.getPrototypeOf, + isEnum: {}.propertyIsEnumerable, + getDesc: $Object.getOwnPropertyDescriptor, + setDesc: $Object.defineProperty, + setDescs: $Object.defineProperties, + getKeys: $Object.keys, + getNames: $Object.getOwnPropertyNames, + getSymbols: $Object.getOwnPropertySymbols, + each: [].forEach + }; + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.registerDefaultHelpers = registerDefaultHelpers; + exports.moveHelperToHooks = moveHelperToHooks; + + var _helpersBlockHelperMissing = __webpack_require__(11); + + var _helpersBlockHelperMissing2 = _interopRequireDefault(_helpersBlockHelperMissing); + + var _helpersEach = __webpack_require__(12); + + var _helpersEach2 = _interopRequireDefault(_helpersEach); + + var _helpersHelperMissing = __webpack_require__(25); + + var _helpersHelperMissing2 = _interopRequireDefault(_helpersHelperMissing); + + var _helpersIf = __webpack_require__(26); + + var _helpersIf2 = _interopRequireDefault(_helpersIf); + + var _helpersLog = __webpack_require__(27); + + var _helpersLog2 = _interopRequireDefault(_helpersLog); + + var _helpersLookup = __webpack_require__(28); + + var _helpersLookup2 = _interopRequireDefault(_helpersLookup); + + var _helpersWith = __webpack_require__(29); + + var _helpersWith2 = _interopRequireDefault(_helpersWith); + + function registerDefaultHelpers(instance) { + _helpersBlockHelperMissing2['default'](instance); + _helpersEach2['default'](instance); + _helpersHelperMissing2['default'](instance); + _helpersIf2['default'](instance); + _helpersLog2['default'](instance); + _helpersLookup2['default'](instance); + _helpersWith2['default'](instance); + } + + function moveHelperToHooks(instance, helperName, keepHelper) { + if (instance.helpers[helperName]) { + instance.hooks[helperName] = instance.helpers[helperName]; + if (!keepHelper) { + delete instance.helpers[helperName]; + } + } + } + +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerHelper('blockHelperMissing', function (context, options) { + var inverse = options.inverse, + fn = options.fn; + + if (context === true) { + return fn(this); + } else if (context === false || context == null) { + return inverse(this); + } else if (_utils.isArray(context)) { + if (context.length > 0) { + if (options.ids) { + options.ids = [options.name]; + } + + return instance.helpers.each(context, options); + } else { + return inverse(this); + } + } else { + if (options.data && options.ids) { + var data = _utils.createFrame(options.data); + data.contextPath = _utils.appendContextPath(options.data.contextPath, options.name); + options = { data: data }; + } + + return fn(context, options); + } + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + var _Object$keys = __webpack_require__(13)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('each', function (context, options) { + if (!options) { + throw new _exception2['default']('Must pass iterator to #each'); + } + + var fn = options.fn, + inverse = options.inverse, + i = 0, + ret = '', + data = undefined, + contextPath = undefined; + + if (options.data && options.ids) { + contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; + } + + if (_utils.isFunction(context)) { + context = context.call(this); + } + + if (options.data) { + data = _utils.createFrame(options.data); + } + + function execIteration(field, index, last) { + if (data) { + data.key = field; + data.index = index; + data.first = index === 0; + data.last = !!last; + + if (contextPath) { + data.contextPath = contextPath + field; + } + } + + ret = ret + fn(context[field], { + data: data, + blockParams: _utils.blockParams([context[field], field], [contextPath + field, null]) + }); + } + + if (context && typeof context === 'object') { + if (_utils.isArray(context)) { + for (var j = context.length; i < j; i++) { + if (i in context) { + execIteration(i, i, i === context.length - 1); + } + } + } else if (global.Symbol && context[global.Symbol.iterator]) { + var newContext = []; + var iterator = context[global.Symbol.iterator](); + for (var it = iterator.next(); !it.done; it = iterator.next()) { + newContext.push(it.value); + } + context = newContext; + for (var j = context.length; i < j; i++) { + execIteration(i, i, i === context.length - 1); + } + } else { + (function () { + var priorKey = undefined; + + _Object$keys(context).forEach(function (key) { + // We're running the iterations one step out of sync so we can detect + // the last iteration without have to scan the object twice and create + // an itermediate keys array. + if (priorKey !== undefined) { + execIteration(priorKey, i - 1); + } + priorKey = key; + i++; + }); + if (priorKey !== undefined) { + execIteration(priorKey, i - 1, true); + } + })(); + } + } + + if (i === 0) { + ret = inverse(this); + } + + return ret; + }); + }; + + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(14), __esModule: true }; + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + + __webpack_require__(15); + module.exports = __webpack_require__(21).Object.keys; + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + + // 19.1.2.14 Object.keys(O) + var toObject = __webpack_require__(16); + + __webpack_require__(18)('keys', function($keys){ + return function keys(it){ + return $keys(toObject(it)); + }; + }); + +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { + + // 7.1.13 ToObject(argument) + var defined = __webpack_require__(17); + module.exports = function(it){ + return Object(defined(it)); + }; + +/***/ }), +/* 17 */ +/***/ (function(module, exports) { + + // 7.2.1 RequireObjectCoercible(argument) + module.exports = function(it){ + if(it == undefined)throw TypeError("Can't call method on " + it); + return it; + }; + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + + // most Object methods by ES6 should accept primitives + var $export = __webpack_require__(19) + , core = __webpack_require__(21) + , fails = __webpack_require__(24); + module.exports = function(KEY, exec){ + var fn = (core.Object || {})[KEY] || Object[KEY] + , exp = {}; + exp[KEY] = exec(fn); + $export($export.S + $export.F * fails(function(){ fn(1); }), 'Object', exp); + }; + +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { + + var global = __webpack_require__(20) + , core = __webpack_require__(21) + , ctx = __webpack_require__(22) + , PROTOTYPE = 'prototype'; + + var $export = function(type, name, source){ + var IS_FORCED = type & $export.F + , IS_GLOBAL = type & $export.G + , IS_STATIC = type & $export.S + , IS_PROTO = type & $export.P + , IS_BIND = type & $export.B + , IS_WRAP = type & $export.W + , exports = IS_GLOBAL ? core : core[name] || (core[name] = {}) + , target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE] + , key, own, out; + if(IS_GLOBAL)source = name; + for(key in source){ + // contains in native + own = !IS_FORCED && target && key in target; + if(own && key in exports)continue; + // export native or passed + out = own ? target[key] : source[key]; + // prevent global pollution for namespaces + exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] + // bind timers to global for call from export context + : IS_BIND && own ? ctx(out, global) + // wrap global constructors for prevent change them in library + : IS_WRAP && target[key] == out ? (function(C){ + var F = function(param){ + return this instanceof C ? new C(param) : C(param); + }; + F[PROTOTYPE] = C[PROTOTYPE]; + return F; + // make static versions for prototype methods + })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; + if(IS_PROTO)(exports[PROTOTYPE] || (exports[PROTOTYPE] = {}))[key] = out; + } + }; + // type bitmap + $export.F = 1; // forced + $export.G = 2; // global + $export.S = 4; // static + $export.P = 8; // proto + $export.B = 16; // bind + $export.W = 32; // wrap + module.exports = $export; + +/***/ }), +/* 20 */ +/***/ (function(module, exports) { + + // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 + var global = module.exports = typeof window != 'undefined' && window.Math == Math + ? window : typeof self != 'undefined' && self.Math == Math ? self : Function('return this')(); + if(typeof __g == 'number')__g = global; // eslint-disable-line no-undef + +/***/ }), +/* 21 */ +/***/ (function(module, exports) { + + var core = module.exports = {version: '1.2.6'}; + if(typeof __e == 'number')__e = core; // eslint-disable-line no-undef + +/***/ }), +/* 22 */ +/***/ (function(module, exports, __webpack_require__) { + + // optional / simple context binding + var aFunction = __webpack_require__(23); + module.exports = function(fn, that, length){ + aFunction(fn); + if(that === undefined)return fn; + switch(length){ + case 1: return function(a){ + return fn.call(that, a); + }; + case 2: return function(a, b){ + return fn.call(that, a, b); + }; + case 3: return function(a, b, c){ + return fn.call(that, a, b, c); + }; + } + return function(/* ...args */){ + return fn.apply(that, arguments); + }; + }; + +/***/ }), +/* 23 */ +/***/ (function(module, exports) { + + module.exports = function(it){ + if(typeof it != 'function')throw TypeError(it + ' is not a function!'); + return it; + }; + +/***/ }), +/* 24 */ +/***/ (function(module, exports) { + + module.exports = function(exec){ + try { + return !!exec(); + } catch(e){ + return true; + } + }; + +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('helperMissing', function () /* [args, ]options */{ + if (arguments.length === 1) { + // A missing field in a {{foo}} construct. + return undefined; + } else { + // Someone is actually trying to call something, blow up. + throw new _exception2['default']('Missing helper: "' + arguments[arguments.length - 1].name + '"'); + } + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 26 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('if', function (conditional, options) { + if (arguments.length != 2) { + throw new _exception2['default']('#if requires exactly one argument'); + } + if (_utils.isFunction(conditional)) { + conditional = conditional.call(this); + } + + // Default behavior is to render the positive path if the value is truthy and not empty. + // The `includeZero` option may be set to treat the condtional as purely not empty based on the + // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative. + if (!options.hash.includeZero && !conditional || _utils.isEmpty(conditional)) { + return options.inverse(this); + } else { + return options.fn(this); + } + }); + + instance.registerHelper('unless', function (conditional, options) { + if (arguments.length != 2) { + throw new _exception2['default']('#unless requires exactly one argument'); + } + return instance.helpers['if'].call(this, conditional, { + fn: options.inverse, + inverse: options.fn, + hash: options.hash + }); + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 27 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (instance) { + instance.registerHelper('log', function () /* message, options */{ + var args = [undefined], + options = arguments[arguments.length - 1]; + for (var i = 0; i < arguments.length - 1; i++) { + args.push(arguments[i]); + } + + var level = 1; + if (options.hash.level != null) { + level = options.hash.level; + } else if (options.data && options.data.level != null) { + level = options.data.level; + } + args[0] = level; + + instance.log.apply(instance, args); + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 28 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (instance) { + instance.registerHelper('lookup', function (obj, field, options) { + if (!obj) { + // Note for 5.0: Change to "obj == null" in 5.0 + return obj; + } + return options.lookupProperty(obj, field); + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('with', function (context, options) { + if (arguments.length != 2) { + throw new _exception2['default']('#with requires exactly one argument'); + } + if (_utils.isFunction(context)) { + context = context.call(this); + } + + var fn = options.fn; + + if (!_utils.isEmpty(context)) { + var data = options.data; + if (options.data && options.ids) { + data = _utils.createFrame(options.data); + data.contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]); + } + + return fn(context, { + data: data, + blockParams: _utils.blockParams([context], [data && data.contextPath]) + }); + } else { + return options.inverse(this); + } + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 30 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.registerDefaultDecorators = registerDefaultDecorators; + + var _decoratorsInline = __webpack_require__(31); + + var _decoratorsInline2 = _interopRequireDefault(_decoratorsInline); + + function registerDefaultDecorators(instance) { + _decoratorsInline2['default'](instance); + } + +/***/ }), +/* 31 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerDecorator('inline', function (fn, props, container, options) { + var ret = fn; + if (!props.partials) { + props.partials = {}; + ret = function (context, options) { + // Create a new partials stack frame prior to exec. + var original = container.partials; + container.partials = _utils.extend({}, original, props.partials); + var ret = fn(context, options); + container.partials = original; + return ret; + }; + } + + props.partials[options.args[0]] = options.fn; + + return ret; + }); + }; + + module.exports = exports['default']; + +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var logger = { + methodMap: ['debug', 'info', 'warn', 'error'], + level: 'info', + + // Maps a given level value to the `methodMap` indexes above. + lookupLevel: function lookupLevel(level) { + if (typeof level === 'string') { + var levelMap = _utils.indexOf(logger.methodMap, level.toLowerCase()); + if (levelMap >= 0) { + level = levelMap; + } else { + level = parseInt(level, 10); + } + } + + return level; + }, + + // Can be overridden in the host environment + log: function log(level) { + level = logger.lookupLevel(level); + + if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) { + var method = logger.methodMap[level]; + // eslint-disable-next-line no-console + if (!console[method]) { + method = 'log'; + } + + for (var _len = arguments.length, message = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + message[_key - 1] = arguments[_key]; + } + + console[method].apply(console, message); // eslint-disable-line no-console + } + } + }; + + exports['default'] = logger; + module.exports = exports['default']; + +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _Object$create = __webpack_require__(34)['default']; + + var _Object$keys = __webpack_require__(13)['default']; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + exports.__esModule = true; + exports.createProtoAccessControl = createProtoAccessControl; + exports.resultIsAllowed = resultIsAllowed; + exports.resetLoggedProperties = resetLoggedProperties; + + var _createNewLookupObject = __webpack_require__(36); + + var _logger = __webpack_require__(32); + + var logger = _interopRequireWildcard(_logger); + + var loggedProperties = _Object$create(null); + + function createProtoAccessControl(runtimeOptions) { + var defaultMethodWhiteList = _Object$create(null); + defaultMethodWhiteList['constructor'] = false; + defaultMethodWhiteList['__defineGetter__'] = false; + defaultMethodWhiteList['__defineSetter__'] = false; + defaultMethodWhiteList['__lookupGetter__'] = false; + + var defaultPropertyWhiteList = _Object$create(null); + // eslint-disable-next-line no-proto + defaultPropertyWhiteList['__proto__'] = false; + + return { + properties: { + whitelist: _createNewLookupObject.createNewLookupObject(defaultPropertyWhiteList, runtimeOptions.allowedProtoProperties), + defaultValue: runtimeOptions.allowProtoPropertiesByDefault + }, + methods: { + whitelist: _createNewLookupObject.createNewLookupObject(defaultMethodWhiteList, runtimeOptions.allowedProtoMethods), + defaultValue: runtimeOptions.allowProtoMethodsByDefault + } + }; + } + + function resultIsAllowed(result, protoAccessControl, propertyName) { + if (typeof result === 'function') { + return checkWhiteList(protoAccessControl.methods, propertyName); + } else { + return checkWhiteList(protoAccessControl.properties, propertyName); + } + } + + function checkWhiteList(protoAccessControlForType, propertyName) { + if (protoAccessControlForType.whitelist[propertyName] !== undefined) { + return protoAccessControlForType.whitelist[propertyName] === true; + } + if (protoAccessControlForType.defaultValue !== undefined) { + return protoAccessControlForType.defaultValue; + } + logUnexpecedPropertyAccessOnce(propertyName); + return false; + } + + function logUnexpecedPropertyAccessOnce(propertyName) { + if (loggedProperties[propertyName] !== true) { + loggedProperties[propertyName] = true; + logger.log('error', 'Handlebars: Access has been denied to resolve the property "' + propertyName + '" because it is not an "own property" of its parent.\n' + 'You can add a runtime option to disable the check or this warning:\n' + 'See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details'); + } + } + + function resetLoggedProperties() { + _Object$keys(loggedProperties).forEach(function (propertyName) { + delete loggedProperties[propertyName]; + }); + } + +/***/ }), +/* 34 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(35), __esModule: true }; + +/***/ }), +/* 35 */ +/***/ (function(module, exports, __webpack_require__) { + + var $ = __webpack_require__(9); + module.exports = function create(P, D){ + return $.create(P, D); + }; + +/***/ }), +/* 36 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _Object$create = __webpack_require__(34)['default']; + + exports.__esModule = true; + exports.createNewLookupObject = createNewLookupObject; + + var _utils = __webpack_require__(5); + + /** + * Create a new object with "null"-prototype to avoid truthy results on prototype properties. + * The resulting object can be used with "object[property]" to check if a property exists + * @param {...object} sources a varargs parameter of source objects that will be merged + * @returns {object} + */ + + function createNewLookupObject() { + for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) { + sources[_key] = arguments[_key]; + } + + return _utils.extend.apply(undefined, [_Object$create(null)].concat(sources)); + } + +/***/ }), +/* 37 */ +/***/ (function(module, exports) { + + // Build out our basic SafeString type + 'use strict'; + + exports.__esModule = true; + function SafeString(string) { + this.string = string; + } + + SafeString.prototype.toString = SafeString.prototype.toHTML = function () { + return '' + this.string; + }; + + exports['default'] = SafeString; + module.exports = exports['default']; + +/***/ }), +/* 38 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _Object$seal = __webpack_require__(39)['default']; + + var _Object$keys = __webpack_require__(13)['default']; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.checkRevision = checkRevision; + exports.template = template; + exports.wrapProgram = wrapProgram; + exports.resolvePartial = resolvePartial; + exports.invokePartial = invokePartial; + exports.noop = noop; + + var _utils = __webpack_require__(5); + + var Utils = _interopRequireWildcard(_utils); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _base = __webpack_require__(4); + + var _helpers = __webpack_require__(10); + + var _internalWrapHelper = __webpack_require__(43); + + var _internalProtoAccess = __webpack_require__(33); + + function checkRevision(compilerInfo) { + var compilerRevision = compilerInfo && compilerInfo[0] || 1, + currentRevision = _base.COMPILER_REVISION; + + if (compilerRevision >= _base.LAST_COMPATIBLE_COMPILER_REVISION && compilerRevision <= _base.COMPILER_REVISION) { + return; + } + + if (compilerRevision < _base.LAST_COMPATIBLE_COMPILER_REVISION) { + var runtimeVersions = _base.REVISION_CHANGES[currentRevision], + compilerVersions = _base.REVISION_CHANGES[compilerRevision]; + throw new _exception2['default']('Template was precompiled with an older version of Handlebars than the current runtime. ' + 'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').'); + } else { + // Use the embedded version info since the runtime doesn't know about this revision yet + throw new _exception2['default']('Template was precompiled with a newer version of Handlebars than the current runtime. ' + 'Please update your runtime to a newer version (' + compilerInfo[1] + ').'); + } + } + + function template(templateSpec, env) { + /* istanbul ignore next */ + if (!env) { + throw new _exception2['default']('No environment passed to template'); + } + if (!templateSpec || !templateSpec.main) { + throw new _exception2['default']('Unknown template object: ' + typeof templateSpec); + } + + templateSpec.main.decorator = templateSpec.main_d; + + // Note: Using env.VM references rather than local var references throughout this section to allow + // for external users to override these as pseudo-supported APIs. + env.VM.checkRevision(templateSpec.compiler); + + // backwards compatibility for precompiled templates with compiler-version 7 (<4.3.0) + var templateWasPrecompiledWithCompilerV7 = templateSpec.compiler && templateSpec.compiler[0] === 7; + + function invokePartialWrapper(partial, context, options) { + if (options.hash) { + context = Utils.extend({}, context, options.hash); + if (options.ids) { + options.ids[0] = true; + } + } + partial = env.VM.resolvePartial.call(this, partial, context, options); + + var extendedOptions = Utils.extend({}, options, { + hooks: this.hooks, + protoAccessControl: this.protoAccessControl + }); + + var result = env.VM.invokePartial.call(this, partial, context, extendedOptions); + + if (result == null && env.compile) { + options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env); + result = options.partials[options.name](context, extendedOptions); + } + if (result != null) { + if (options.indent) { + var lines = result.split('\n'); + for (var i = 0, l = lines.length; i < l; i++) { + if (!lines[i] && i + 1 === l) { + break; + } + + lines[i] = options.indent + lines[i]; + } + result = lines.join('\n'); + } + return result; + } else { + throw new _exception2['default']('The partial ' + options.name + ' could not be compiled when running in runtime-only mode'); + } + } + + // Just add water + var container = { + strict: function strict(obj, name, loc) { + if (!obj || !(name in obj)) { + throw new _exception2['default']('"' + name + '" not defined in ' + obj, { + loc: loc + }); + } + return container.lookupProperty(obj, name); + }, + lookupProperty: function lookupProperty(parent, propertyName) { + var result = parent[propertyName]; + if (result == null) { + return result; + } + if (Object.prototype.hasOwnProperty.call(parent, propertyName)) { + return result; + } + + if (_internalProtoAccess.resultIsAllowed(result, container.protoAccessControl, propertyName)) { + return result; + } + return undefined; + }, + lookup: function lookup(depths, name) { + var len = depths.length; + for (var i = 0; i < len; i++) { + var result = depths[i] && container.lookupProperty(depths[i], name); + if (result != null) { + return depths[i][name]; + } + } + }, + lambda: function lambda(current, context) { + return typeof current === 'function' ? current.call(context) : current; + }, + + escapeExpression: Utils.escapeExpression, + invokePartial: invokePartialWrapper, + + fn: function fn(i) { + var ret = templateSpec[i]; + ret.decorator = templateSpec[i + '_d']; + return ret; + }, + + programs: [], + program: function program(i, data, declaredBlockParams, blockParams, depths) { + var programWrapper = this.programs[i], + fn = this.fn(i); + if (data || depths || blockParams || declaredBlockParams) { + programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths); + } else if (!programWrapper) { + programWrapper = this.programs[i] = wrapProgram(this, i, fn); + } + return programWrapper; + }, + + data: function data(value, depth) { + while (value && depth--) { + value = value._parent; + } + return value; + }, + mergeIfNeeded: function mergeIfNeeded(param, common) { + var obj = param || common; + + if (param && common && param !== common) { + obj = Utils.extend({}, common, param); + } + + return obj; + }, + // An empty object to use as replacement for null-contexts + nullContext: _Object$seal({}), + + noop: env.VM.noop, + compilerInfo: templateSpec.compiler + }; + + function ret(context) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + var data = options.data; + + ret._setup(options); + if (!options.partial && templateSpec.useData) { + data = initData(context, data); + } + var depths = undefined, + blockParams = templateSpec.useBlockParams ? [] : undefined; + if (templateSpec.useDepths) { + if (options.depths) { + depths = context != options.depths[0] ? [context].concat(options.depths) : options.depths; + } else { + depths = [context]; + } + } + + function main(context /*, options*/) { + return '' + templateSpec.main(container, context, container.helpers, container.partials, data, blockParams, depths); + } + + main = executeDecorators(templateSpec.main, main, container, options.depths || [], data, blockParams); + return main(context, options); + } + + ret.isTop = true; + + ret._setup = function (options) { + if (!options.partial) { + var mergedHelpers = Utils.extend({}, env.helpers, options.helpers); + wrapHelpersToPassLookupProperty(mergedHelpers, container); + container.helpers = mergedHelpers; + + if (templateSpec.usePartial) { + // Use mergeIfNeeded here to prevent compiling global partials multiple times + container.partials = container.mergeIfNeeded(options.partials, env.partials); + } + if (templateSpec.usePartial || templateSpec.useDecorators) { + container.decorators = Utils.extend({}, env.decorators, options.decorators); + } + + container.hooks = {}; + container.protoAccessControl = _internalProtoAccess.createProtoAccessControl(options); + + var keepHelperInHelpers = options.allowCallsToHelperMissing || templateWasPrecompiledWithCompilerV7; + _helpers.moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers); + _helpers.moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers); + } else { + container.protoAccessControl = options.protoAccessControl; // internal option + container.helpers = options.helpers; + container.partials = options.partials; + container.decorators = options.decorators; + container.hooks = options.hooks; + } + }; + + ret._child = function (i, data, blockParams, depths) { + if (templateSpec.useBlockParams && !blockParams) { + throw new _exception2['default']('must pass block params'); + } + if (templateSpec.useDepths && !depths) { + throw new _exception2['default']('must pass parent depths'); + } + + return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths); + }; + return ret; + } + + function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) { + function prog(context) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + var currentDepths = depths; + if (depths && context != depths[0] && !(context === container.nullContext && depths[0] === null)) { + currentDepths = [context].concat(depths); + } + + return fn(container, context, container.helpers, container.partials, options.data || data, blockParams && [options.blockParams].concat(blockParams), currentDepths); + } + + prog = executeDecorators(fn, prog, container, depths, data, blockParams); + + prog.program = i; + prog.depth = depths ? depths.length : 0; + prog.blockParams = declaredBlockParams || 0; + return prog; + } + + /** + * This is currently part of the official API, therefore implementation details should not be changed. + */ + + function resolvePartial(partial, context, options) { + if (!partial) { + if (options.name === '@partial-block') { + partial = options.data['partial-block']; + } else { + partial = options.partials[options.name]; + } + } else if (!partial.call && !options.name) { + // This is a dynamic partial that returned a string + options.name = partial; + partial = options.partials[partial]; + } + return partial; + } + + function invokePartial(partial, context, options) { + // Use the current closure context to save the partial-block if this partial + var currentPartialBlock = options.data && options.data['partial-block']; + options.partial = true; + if (options.ids) { + options.data.contextPath = options.ids[0] || options.data.contextPath; + } + + var partialBlock = undefined; + if (options.fn && options.fn !== noop) { + (function () { + options.data = _base.createFrame(options.data); + // Wrapper function to get access to currentPartialBlock from the closure + var fn = options.fn; + partialBlock = options.data['partial-block'] = function partialBlockWrapper(context) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + // Restore the partial-block from the closure for the execution of the block + // i.e. the part inside the block of the partial call. + options.data = _base.createFrame(options.data); + options.data['partial-block'] = currentPartialBlock; + return fn(context, options); + }; + if (fn.partials) { + options.partials = Utils.extend({}, options.partials, fn.partials); + } + })(); + } + + if (partial === undefined && partialBlock) { + partial = partialBlock; + } + + if (partial === undefined) { + throw new _exception2['default']('The partial ' + options.name + ' could not be found'); + } else if (partial instanceof Function) { + return partial(context, options); + } + } + + function noop() { + return ''; + } + + function initData(context, data) { + if (!data || !('root' in data)) { + data = data ? _base.createFrame(data) : {}; + data.root = context; + } + return data; + } + + function executeDecorators(fn, prog, container, depths, data, blockParams) { + if (fn.decorator) { + var props = {}; + prog = fn.decorator(prog, props, container, depths && depths[0], data, blockParams, depths); + Utils.extend(prog, props); + } + return prog; + } + + function wrapHelpersToPassLookupProperty(mergedHelpers, container) { + _Object$keys(mergedHelpers).forEach(function (helperName) { + var helper = mergedHelpers[helperName]; + mergedHelpers[helperName] = passLookupPropertyOption(helper, container); + }); + } + + function passLookupPropertyOption(helper, container) { + var lookupProperty = container.lookupProperty; + return _internalWrapHelper.wrapHelper(helper, function (options) { + return Utils.extend({ lookupProperty: lookupProperty }, options); + }); + } + +/***/ }), +/* 39 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = { "default": __webpack_require__(40), __esModule: true }; + +/***/ }), +/* 40 */ +/***/ (function(module, exports, __webpack_require__) { + + __webpack_require__(41); + module.exports = __webpack_require__(21).Object.seal; + +/***/ }), +/* 41 */ +/***/ (function(module, exports, __webpack_require__) { + + // 19.1.2.17 Object.seal(O) + var isObject = __webpack_require__(42); + + __webpack_require__(18)('seal', function($seal){ + return function seal(it){ + return $seal && isObject(it) ? $seal(it) : it; + }; + }); + +/***/ }), +/* 42 */ +/***/ (function(module, exports) { + + module.exports = function(it){ + return typeof it === 'object' ? it !== null : typeof it === 'function'; + }; + +/***/ }), +/* 43 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + exports.wrapHelper = wrapHelper; + + function wrapHelper(helper, transformOptionsFn) { + if (typeof helper !== 'function') { + // This should not happen, but apparently it does in https://github.com/wycats/handlebars.js/issues/1639 + // We try to make the wrapper least-invasive by not wrapping it, if the helper is not a function. + return helper; + } + var wrapper = function wrapper() /* dynamic arguments */{ + var options = arguments[arguments.length - 1]; + arguments[arguments.length - 1] = transformOptionsFn(options); + return helper.apply(this, arguments); + }; + return wrapper; + } + +/***/ }), +/* 44 */ +/***/ (function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + exports.__esModule = true; + + exports['default'] = function (Handlebars) { + /* istanbul ignore next */ + var root = typeof global !== 'undefined' ? global : window, + $Handlebars = root.Handlebars; + /* istanbul ignore next */ + Handlebars.noConflict = function () { + if (root.Handlebars === Handlebars) { + root.Handlebars = $Handlebars; + } + return Handlebars; + }; + }; + + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }), +/* 45 */ +/***/ (function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + var AST = { + // Public API used to evaluate derived attributes regarding AST nodes + helpers: { + // a mustache is definitely a helper if: + // * it is an eligible helper, and + // * it has at least one parameter or hash segment + helperExpression: function helperExpression(node) { + return node.type === 'SubExpression' || (node.type === 'MustacheStatement' || node.type === 'BlockStatement') && !!(node.params && node.params.length || node.hash); + }, + + scopedId: function scopedId(path) { + return (/^\.|this\b/.test(path.original) + ); + }, + + // an ID is simple if it only has one part, and that part is not + // `..` or `this`. + simpleId: function simpleId(path) { + return path.parts.length === 1 && !AST.helpers.scopedId(path) && !path.depth; + } + } + }; + + // Must be exported as an object rather than the root of the module as the jison lexer + // must modify the object to operate properly. + exports['default'] = AST; + module.exports = exports['default']; + +/***/ }), +/* 46 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + exports.__esModule = true; + exports.parseWithoutProcessing = parseWithoutProcessing; + exports.parse = parse; + + var _parser = __webpack_require__(47); + + var _parser2 = _interopRequireDefault(_parser); + + var _whitespaceControl = __webpack_require__(48); + + var _whitespaceControl2 = _interopRequireDefault(_whitespaceControl); + + var _helpers = __webpack_require__(50); + + var Helpers = _interopRequireWildcard(_helpers); + + var _utils = __webpack_require__(5); + + exports.parser = _parser2['default']; + + var yy = {}; + _utils.extend(yy, Helpers); + + function parseWithoutProcessing(input, options) { + // Just return if an already-compiled AST was passed in. + if (input.type === 'Program') { + return input; + } + + _parser2['default'].yy = yy; + + // Altering the shared object here, but this is ok as parser is a sync operation + yy.locInfo = function (locInfo) { + return new yy.SourceLocation(options && options.srcName, locInfo); + }; + + var ast = _parser2['default'].parse(input); + + return ast; + } + + function parse(input, options) { + var ast = parseWithoutProcessing(input, options); + var strip = new _whitespaceControl2['default'](options); + + return strip.accept(ast); + } + +/***/ }), +/* 47 */ +/***/ (function(module, exports) { + + // File ignored in coverage tests via setting in .istanbul.yml + /* Jison generated parser */ + "use strict"; + + exports.__esModule = true; + var handlebars = (function () { + var parser = { trace: function trace() {}, + yy: {}, + symbols_: { "error": 2, "root": 3, "program": 4, "EOF": 5, "program_repetition0": 6, "statement": 7, "mustache": 8, "block": 9, "rawBlock": 10, "partial": 11, "partialBlock": 12, "content": 13, "COMMENT": 14, "CONTENT": 15, "openRawBlock": 16, "rawBlock_repetition0": 17, "END_RAW_BLOCK": 18, "OPEN_RAW_BLOCK": 19, "helperName": 20, "openRawBlock_repetition0": 21, "openRawBlock_option0": 22, "CLOSE_RAW_BLOCK": 23, "openBlock": 24, "block_option0": 25, "closeBlock": 26, "openInverse": 27, "block_option1": 28, "OPEN_BLOCK": 29, "openBlock_repetition0": 30, "openBlock_option0": 31, "openBlock_option1": 32, "CLOSE": 33, "OPEN_INVERSE": 34, "openInverse_repetition0": 35, "openInverse_option0": 36, "openInverse_option1": 37, "openInverseChain": 38, "OPEN_INVERSE_CHAIN": 39, "openInverseChain_repetition0": 40, "openInverseChain_option0": 41, "openInverseChain_option1": 42, "inverseAndProgram": 43, "INVERSE": 44, "inverseChain": 45, "inverseChain_option0": 46, "OPEN_ENDBLOCK": 47, "OPEN": 48, "mustache_repetition0": 49, "mustache_option0": 50, "OPEN_UNESCAPED": 51, "mustache_repetition1": 52, "mustache_option1": 53, "CLOSE_UNESCAPED": 54, "OPEN_PARTIAL": 55, "partialName": 56, "partial_repetition0": 57, "partial_option0": 58, "openPartialBlock": 59, "OPEN_PARTIAL_BLOCK": 60, "openPartialBlock_repetition0": 61, "openPartialBlock_option0": 62, "param": 63, "sexpr": 64, "OPEN_SEXPR": 65, "sexpr_repetition0": 66, "sexpr_option0": 67, "CLOSE_SEXPR": 68, "hash": 69, "hash_repetition_plus0": 70, "hashSegment": 71, "ID": 72, "EQUALS": 73, "blockParams": 74, "OPEN_BLOCK_PARAMS": 75, "blockParams_repetition_plus0": 76, "CLOSE_BLOCK_PARAMS": 77, "path": 78, "dataName": 79, "STRING": 80, "NUMBER": 81, "BOOLEAN": 82, "UNDEFINED": 83, "NULL": 84, "DATA": 85, "pathSegments": 86, "SEP": 87, "$accept": 0, "$end": 1 }, + terminals_: { 2: "error", 5: "EOF", 14: "COMMENT", 15: "CONTENT", 18: "END_RAW_BLOCK", 19: "OPEN_RAW_BLOCK", 23: "CLOSE_RAW_BLOCK", 29: "OPEN_BLOCK", 33: "CLOSE", 34: "OPEN_INVERSE", 39: "OPEN_INVERSE_CHAIN", 44: "INVERSE", 47: "OPEN_ENDBLOCK", 48: "OPEN", 51: "OPEN_UNESCAPED", 54: "CLOSE_UNESCAPED", 55: "OPEN_PARTIAL", 60: "OPEN_PARTIAL_BLOCK", 65: "OPEN_SEXPR", 68: "CLOSE_SEXPR", 72: "ID", 73: "EQUALS", 75: "OPEN_BLOCK_PARAMS", 77: "CLOSE_BLOCK_PARAMS", 80: "STRING", 81: "NUMBER", 82: "BOOLEAN", 83: "UNDEFINED", 84: "NULL", 85: "DATA", 87: "SEP" }, + productions_: [0, [3, 2], [4, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [13, 1], [10, 3], [16, 5], [9, 4], [9, 4], [24, 6], [27, 6], [38, 6], [43, 2], [45, 3], [45, 1], [26, 3], [8, 5], [8, 5], [11, 5], [12, 3], [59, 5], [63, 1], [63, 1], [64, 5], [69, 1], [71, 3], [74, 3], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [56, 1], [56, 1], [79, 2], [78, 1], [86, 3], [86, 1], [6, 0], [6, 2], [17, 0], [17, 2], [21, 0], [21, 2], [22, 0], [22, 1], [25, 0], [25, 1], [28, 0], [28, 1], [30, 0], [30, 2], [31, 0], [31, 1], [32, 0], [32, 1], [35, 0], [35, 2], [36, 0], [36, 1], [37, 0], [37, 1], [40, 0], [40, 2], [41, 0], [41, 1], [42, 0], [42, 1], [46, 0], [46, 1], [49, 0], [49, 2], [50, 0], [50, 1], [52, 0], [52, 2], [53, 0], [53, 1], [57, 0], [57, 2], [58, 0], [58, 1], [61, 0], [61, 2], [62, 0], [62, 1], [66, 0], [66, 2], [67, 0], [67, 1], [70, 1], [70, 2], [76, 1], [76, 2]], + performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$) { + + var $0 = $$.length - 1; + switch (yystate) { + case 1: + return $$[$0 - 1]; + break; + case 2: + this.$ = yy.prepareProgram($$[$0]); + break; + case 3: + this.$ = $$[$0]; + break; + case 4: + this.$ = $$[$0]; + break; + case 5: + this.$ = $$[$0]; + break; + case 6: + this.$ = $$[$0]; + break; + case 7: + this.$ = $$[$0]; + break; + case 8: + this.$ = $$[$0]; + break; + case 9: + this.$ = { + type: 'CommentStatement', + value: yy.stripComment($$[$0]), + strip: yy.stripFlags($$[$0], $$[$0]), + loc: yy.locInfo(this._$) + }; + + break; + case 10: + this.$ = { + type: 'ContentStatement', + original: $$[$0], + value: $$[$0], + loc: yy.locInfo(this._$) + }; + + break; + case 11: + this.$ = yy.prepareRawBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$); + break; + case 12: + this.$ = { path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1] }; + break; + case 13: + this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], false, this._$); + break; + case 14: + this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], true, this._$); + break; + case 15: + this.$ = { open: $$[$0 - 5], path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 16: + this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 17: + this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 18: + this.$ = { strip: yy.stripFlags($$[$0 - 1], $$[$0 - 1]), program: $$[$0] }; + break; + case 19: + var inverse = yy.prepareBlock($$[$0 - 2], $$[$0 - 1], $$[$0], $$[$0], false, this._$), + program = yy.prepareProgram([inverse], $$[$0 - 1].loc); + program.chained = true; + + this.$ = { strip: $$[$0 - 2].strip, program: program, chain: true }; + + break; + case 20: + this.$ = $$[$0]; + break; + case 21: + this.$ = { path: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 2], $$[$0]) }; + break; + case 22: + this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$); + break; + case 23: + this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$); + break; + case 24: + this.$ = { + type: 'PartialStatement', + name: $$[$0 - 3], + params: $$[$0 - 2], + hash: $$[$0 - 1], + indent: '', + strip: yy.stripFlags($$[$0 - 4], $$[$0]), + loc: yy.locInfo(this._$) + }; + + break; + case 25: + this.$ = yy.preparePartialBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$); + break; + case 26: + this.$ = { path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 4], $$[$0]) }; + break; + case 27: + this.$ = $$[$0]; + break; + case 28: + this.$ = $$[$0]; + break; + case 29: + this.$ = { + type: 'SubExpression', + path: $$[$0 - 3], + params: $$[$0 - 2], + hash: $$[$0 - 1], + loc: yy.locInfo(this._$) + }; + + break; + case 30: + this.$ = { type: 'Hash', pairs: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 31: + this.$ = { type: 'HashPair', key: yy.id($$[$0 - 2]), value: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 32: + this.$ = yy.id($$[$0 - 1]); + break; + case 33: + this.$ = $$[$0]; + break; + case 34: + this.$ = $$[$0]; + break; + case 35: + this.$ = { type: 'StringLiteral', value: $$[$0], original: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 36: + this.$ = { type: 'NumberLiteral', value: Number($$[$0]), original: Number($$[$0]), loc: yy.locInfo(this._$) }; + break; + case 37: + this.$ = { type: 'BooleanLiteral', value: $$[$0] === 'true', original: $$[$0] === 'true', loc: yy.locInfo(this._$) }; + break; + case 38: + this.$ = { type: 'UndefinedLiteral', original: undefined, value: undefined, loc: yy.locInfo(this._$) }; + break; + case 39: + this.$ = { type: 'NullLiteral', original: null, value: null, loc: yy.locInfo(this._$) }; + break; + case 40: + this.$ = $$[$0]; + break; + case 41: + this.$ = $$[$0]; + break; + case 42: + this.$ = yy.preparePath(true, $$[$0], this._$); + break; + case 43: + this.$ = yy.preparePath(false, $$[$0], this._$); + break; + case 44: + $$[$0 - 2].push({ part: yy.id($$[$0]), original: $$[$0], separator: $$[$0 - 1] });this.$ = $$[$0 - 2]; + break; + case 45: + this.$ = [{ part: yy.id($$[$0]), original: $$[$0] }]; + break; + case 46: + this.$ = []; + break; + case 47: + $$[$0 - 1].push($$[$0]); + break; + case 48: + this.$ = []; + break; + case 49: + $$[$0 - 1].push($$[$0]); + break; + case 50: + this.$ = []; + break; + case 51: + $$[$0 - 1].push($$[$0]); + break; + case 58: + this.$ = []; + break; + case 59: + $$[$0 - 1].push($$[$0]); + break; + case 64: + this.$ = []; + break; + case 65: + $$[$0 - 1].push($$[$0]); + break; + case 70: + this.$ = []; + break; + case 71: + $$[$0 - 1].push($$[$0]); + break; + case 78: + this.$ = []; + break; + case 79: + $$[$0 - 1].push($$[$0]); + break; + case 82: + this.$ = []; + break; + case 83: + $$[$0 - 1].push($$[$0]); + break; + case 86: + this.$ = []; + break; + case 87: + $$[$0 - 1].push($$[$0]); + break; + case 90: + this.$ = []; + break; + case 91: + $$[$0 - 1].push($$[$0]); + break; + case 94: + this.$ = []; + break; + case 95: + $$[$0 - 1].push($$[$0]); + break; + case 98: + this.$ = [$$[$0]]; + break; + case 99: + $$[$0 - 1].push($$[$0]); + break; + case 100: + this.$ = [$$[$0]]; + break; + case 101: + $$[$0 - 1].push($$[$0]); + break; + } + }, + table: [{ 3: 1, 4: 2, 5: [2, 46], 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 1: [3] }, { 5: [1, 4] }, { 5: [2, 2], 7: 5, 8: 6, 9: 7, 10: 8, 11: 9, 12: 10, 13: 11, 14: [1, 12], 15: [1, 20], 16: 17, 19: [1, 23], 24: 15, 27: 16, 29: [1, 21], 34: [1, 22], 39: [2, 2], 44: [2, 2], 47: [2, 2], 48: [1, 13], 51: [1, 14], 55: [1, 18], 59: 19, 60: [1, 24] }, { 1: [2, 1] }, { 5: [2, 47], 14: [2, 47], 15: [2, 47], 19: [2, 47], 29: [2, 47], 34: [2, 47], 39: [2, 47], 44: [2, 47], 47: [2, 47], 48: [2, 47], 51: [2, 47], 55: [2, 47], 60: [2, 47] }, { 5: [2, 3], 14: [2, 3], 15: [2, 3], 19: [2, 3], 29: [2, 3], 34: [2, 3], 39: [2, 3], 44: [2, 3], 47: [2, 3], 48: [2, 3], 51: [2, 3], 55: [2, 3], 60: [2, 3] }, { 5: [2, 4], 14: [2, 4], 15: [2, 4], 19: [2, 4], 29: [2, 4], 34: [2, 4], 39: [2, 4], 44: [2, 4], 47: [2, 4], 48: [2, 4], 51: [2, 4], 55: [2, 4], 60: [2, 4] }, { 5: [2, 5], 14: [2, 5], 15: [2, 5], 19: [2, 5], 29: [2, 5], 34: [2, 5], 39: [2, 5], 44: [2, 5], 47: [2, 5], 48: [2, 5], 51: [2, 5], 55: [2, 5], 60: [2, 5] }, { 5: [2, 6], 14: [2, 6], 15: [2, 6], 19: [2, 6], 29: [2, 6], 34: [2, 6], 39: [2, 6], 44: [2, 6], 47: [2, 6], 48: [2, 6], 51: [2, 6], 55: [2, 6], 60: [2, 6] }, { 5: [2, 7], 14: [2, 7], 15: [2, 7], 19: [2, 7], 29: [2, 7], 34: [2, 7], 39: [2, 7], 44: [2, 7], 47: [2, 7], 48: [2, 7], 51: [2, 7], 55: [2, 7], 60: [2, 7] }, { 5: [2, 8], 14: [2, 8], 15: [2, 8], 19: [2, 8], 29: [2, 8], 34: [2, 8], 39: [2, 8], 44: [2, 8], 47: [2, 8], 48: [2, 8], 51: [2, 8], 55: [2, 8], 60: [2, 8] }, { 5: [2, 9], 14: [2, 9], 15: [2, 9], 19: [2, 9], 29: [2, 9], 34: [2, 9], 39: [2, 9], 44: [2, 9], 47: [2, 9], 48: [2, 9], 51: [2, 9], 55: [2, 9], 60: [2, 9] }, { 20: 25, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 36, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 37, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 39: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 4: 38, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 15: [2, 48], 17: 39, 18: [2, 48] }, { 20: 41, 56: 40, 64: 42, 65: [1, 43], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 44, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 5: [2, 10], 14: [2, 10], 15: [2, 10], 18: [2, 10], 19: [2, 10], 29: [2, 10], 34: [2, 10], 39: [2, 10], 44: [2, 10], 47: [2, 10], 48: [2, 10], 51: [2, 10], 55: [2, 10], 60: [2, 10] }, { 20: 45, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 46, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 47, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 41, 56: 48, 64: 42, 65: [1, 43], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [2, 78], 49: 49, 65: [2, 78], 72: [2, 78], 80: [2, 78], 81: [2, 78], 82: [2, 78], 83: [2, 78], 84: [2, 78], 85: [2, 78] }, { 23: [2, 33], 33: [2, 33], 54: [2, 33], 65: [2, 33], 68: [2, 33], 72: [2, 33], 75: [2, 33], 80: [2, 33], 81: [2, 33], 82: [2, 33], 83: [2, 33], 84: [2, 33], 85: [2, 33] }, { 23: [2, 34], 33: [2, 34], 54: [2, 34], 65: [2, 34], 68: [2, 34], 72: [2, 34], 75: [2, 34], 80: [2, 34], 81: [2, 34], 82: [2, 34], 83: [2, 34], 84: [2, 34], 85: [2, 34] }, { 23: [2, 35], 33: [2, 35], 54: [2, 35], 65: [2, 35], 68: [2, 35], 72: [2, 35], 75: [2, 35], 80: [2, 35], 81: [2, 35], 82: [2, 35], 83: [2, 35], 84: [2, 35], 85: [2, 35] }, { 23: [2, 36], 33: [2, 36], 54: [2, 36], 65: [2, 36], 68: [2, 36], 72: [2, 36], 75: [2, 36], 80: [2, 36], 81: [2, 36], 82: [2, 36], 83: [2, 36], 84: [2, 36], 85: [2, 36] }, { 23: [2, 37], 33: [2, 37], 54: [2, 37], 65: [2, 37], 68: [2, 37], 72: [2, 37], 75: [2, 37], 80: [2, 37], 81: [2, 37], 82: [2, 37], 83: [2, 37], 84: [2, 37], 85: [2, 37] }, { 23: [2, 38], 33: [2, 38], 54: [2, 38], 65: [2, 38], 68: [2, 38], 72: [2, 38], 75: [2, 38], 80: [2, 38], 81: [2, 38], 82: [2, 38], 83: [2, 38], 84: [2, 38], 85: [2, 38] }, { 23: [2, 39], 33: [2, 39], 54: [2, 39], 65: [2, 39], 68: [2, 39], 72: [2, 39], 75: [2, 39], 80: [2, 39], 81: [2, 39], 82: [2, 39], 83: [2, 39], 84: [2, 39], 85: [2, 39] }, { 23: [2, 43], 33: [2, 43], 54: [2, 43], 65: [2, 43], 68: [2, 43], 72: [2, 43], 75: [2, 43], 80: [2, 43], 81: [2, 43], 82: [2, 43], 83: [2, 43], 84: [2, 43], 85: [2, 43], 87: [1, 50] }, { 72: [1, 35], 86: 51 }, { 23: [2, 45], 33: [2, 45], 54: [2, 45], 65: [2, 45], 68: [2, 45], 72: [2, 45], 75: [2, 45], 80: [2, 45], 81: [2, 45], 82: [2, 45], 83: [2, 45], 84: [2, 45], 85: [2, 45], 87: [2, 45] }, { 52: 52, 54: [2, 82], 65: [2, 82], 72: [2, 82], 80: [2, 82], 81: [2, 82], 82: [2, 82], 83: [2, 82], 84: [2, 82], 85: [2, 82] }, { 25: 53, 38: 55, 39: [1, 57], 43: 56, 44: [1, 58], 45: 54, 47: [2, 54] }, { 28: 59, 43: 60, 44: [1, 58], 47: [2, 56] }, { 13: 62, 15: [1, 20], 18: [1, 61] }, { 33: [2, 86], 57: 63, 65: [2, 86], 72: [2, 86], 80: [2, 86], 81: [2, 86], 82: [2, 86], 83: [2, 86], 84: [2, 86], 85: [2, 86] }, { 33: [2, 40], 65: [2, 40], 72: [2, 40], 80: [2, 40], 81: [2, 40], 82: [2, 40], 83: [2, 40], 84: [2, 40], 85: [2, 40] }, { 33: [2, 41], 65: [2, 41], 72: [2, 41], 80: [2, 41], 81: [2, 41], 82: [2, 41], 83: [2, 41], 84: [2, 41], 85: [2, 41] }, { 20: 64, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 26: 65, 47: [1, 66] }, { 30: 67, 33: [2, 58], 65: [2, 58], 72: [2, 58], 75: [2, 58], 80: [2, 58], 81: [2, 58], 82: [2, 58], 83: [2, 58], 84: [2, 58], 85: [2, 58] }, { 33: [2, 64], 35: 68, 65: [2, 64], 72: [2, 64], 75: [2, 64], 80: [2, 64], 81: [2, 64], 82: [2, 64], 83: [2, 64], 84: [2, 64], 85: [2, 64] }, { 21: 69, 23: [2, 50], 65: [2, 50], 72: [2, 50], 80: [2, 50], 81: [2, 50], 82: [2, 50], 83: [2, 50], 84: [2, 50], 85: [2, 50] }, { 33: [2, 90], 61: 70, 65: [2, 90], 72: [2, 90], 80: [2, 90], 81: [2, 90], 82: [2, 90], 83: [2, 90], 84: [2, 90], 85: [2, 90] }, { 20: 74, 33: [2, 80], 50: 71, 63: 72, 64: 75, 65: [1, 43], 69: 73, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 72: [1, 79] }, { 23: [2, 42], 33: [2, 42], 54: [2, 42], 65: [2, 42], 68: [2, 42], 72: [2, 42], 75: [2, 42], 80: [2, 42], 81: [2, 42], 82: [2, 42], 83: [2, 42], 84: [2, 42], 85: [2, 42], 87: [1, 50] }, { 20: 74, 53: 80, 54: [2, 84], 63: 81, 64: 75, 65: [1, 43], 69: 82, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 26: 83, 47: [1, 66] }, { 47: [2, 55] }, { 4: 84, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 39: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 47: [2, 20] }, { 20: 85, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 86, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 26: 87, 47: [1, 66] }, { 47: [2, 57] }, { 5: [2, 11], 14: [2, 11], 15: [2, 11], 19: [2, 11], 29: [2, 11], 34: [2, 11], 39: [2, 11], 44: [2, 11], 47: [2, 11], 48: [2, 11], 51: [2, 11], 55: [2, 11], 60: [2, 11] }, { 15: [2, 49], 18: [2, 49] }, { 20: 74, 33: [2, 88], 58: 88, 63: 89, 64: 75, 65: [1, 43], 69: 90, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 65: [2, 94], 66: 91, 68: [2, 94], 72: [2, 94], 80: [2, 94], 81: [2, 94], 82: [2, 94], 83: [2, 94], 84: [2, 94], 85: [2, 94] }, { 5: [2, 25], 14: [2, 25], 15: [2, 25], 19: [2, 25], 29: [2, 25], 34: [2, 25], 39: [2, 25], 44: [2, 25], 47: [2, 25], 48: [2, 25], 51: [2, 25], 55: [2, 25], 60: [2, 25] }, { 20: 92, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 74, 31: 93, 33: [2, 60], 63: 94, 64: 75, 65: [1, 43], 69: 95, 70: 76, 71: 77, 72: [1, 78], 75: [2, 60], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 74, 33: [2, 66], 36: 96, 63: 97, 64: 75, 65: [1, 43], 69: 98, 70: 76, 71: 77, 72: [1, 78], 75: [2, 66], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 74, 22: 99, 23: [2, 52], 63: 100, 64: 75, 65: [1, 43], 69: 101, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 74, 33: [2, 92], 62: 102, 63: 103, 64: 75, 65: [1, 43], 69: 104, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [1, 105] }, { 33: [2, 79], 65: [2, 79], 72: [2, 79], 80: [2, 79], 81: [2, 79], 82: [2, 79], 83: [2, 79], 84: [2, 79], 85: [2, 79] }, { 33: [2, 81] }, { 23: [2, 27], 33: [2, 27], 54: [2, 27], 65: [2, 27], 68: [2, 27], 72: [2, 27], 75: [2, 27], 80: [2, 27], 81: [2, 27], 82: [2, 27], 83: [2, 27], 84: [2, 27], 85: [2, 27] }, { 23: [2, 28], 33: [2, 28], 54: [2, 28], 65: [2, 28], 68: [2, 28], 72: [2, 28], 75: [2, 28], 80: [2, 28], 81: [2, 28], 82: [2, 28], 83: [2, 28], 84: [2, 28], 85: [2, 28] }, { 23: [2, 30], 33: [2, 30], 54: [2, 30], 68: [2, 30], 71: 106, 72: [1, 107], 75: [2, 30] }, { 23: [2, 98], 33: [2, 98], 54: [2, 98], 68: [2, 98], 72: [2, 98], 75: [2, 98] }, { 23: [2, 45], 33: [2, 45], 54: [2, 45], 65: [2, 45], 68: [2, 45], 72: [2, 45], 73: [1, 108], 75: [2, 45], 80: [2, 45], 81: [2, 45], 82: [2, 45], 83: [2, 45], 84: [2, 45], 85: [2, 45], 87: [2, 45] }, { 23: [2, 44], 33: [2, 44], 54: [2, 44], 65: [2, 44], 68: [2, 44], 72: [2, 44], 75: [2, 44], 80: [2, 44], 81: [2, 44], 82: [2, 44], 83: [2, 44], 84: [2, 44], 85: [2, 44], 87: [2, 44] }, { 54: [1, 109] }, { 54: [2, 83], 65: [2, 83], 72: [2, 83], 80: [2, 83], 81: [2, 83], 82: [2, 83], 83: [2, 83], 84: [2, 83], 85: [2, 83] }, { 54: [2, 85] }, { 5: [2, 13], 14: [2, 13], 15: [2, 13], 19: [2, 13], 29: [2, 13], 34: [2, 13], 39: [2, 13], 44: [2, 13], 47: [2, 13], 48: [2, 13], 51: [2, 13], 55: [2, 13], 60: [2, 13] }, { 38: 55, 39: [1, 57], 43: 56, 44: [1, 58], 45: 111, 46: 110, 47: [2, 76] }, { 33: [2, 70], 40: 112, 65: [2, 70], 72: [2, 70], 75: [2, 70], 80: [2, 70], 81: [2, 70], 82: [2, 70], 83: [2, 70], 84: [2, 70], 85: [2, 70] }, { 47: [2, 18] }, { 5: [2, 14], 14: [2, 14], 15: [2, 14], 19: [2, 14], 29: [2, 14], 34: [2, 14], 39: [2, 14], 44: [2, 14], 47: [2, 14], 48: [2, 14], 51: [2, 14], 55: [2, 14], 60: [2, 14] }, { 33: [1, 113] }, { 33: [2, 87], 65: [2, 87], 72: [2, 87], 80: [2, 87], 81: [2, 87], 82: [2, 87], 83: [2, 87], 84: [2, 87], 85: [2, 87] }, { 33: [2, 89] }, { 20: 74, 63: 115, 64: 75, 65: [1, 43], 67: 114, 68: [2, 96], 69: 116, 70: 76, 71: 77, 72: [1, 78], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [1, 117] }, { 32: 118, 33: [2, 62], 74: 119, 75: [1, 120] }, { 33: [2, 59], 65: [2, 59], 72: [2, 59], 75: [2, 59], 80: [2, 59], 81: [2, 59], 82: [2, 59], 83: [2, 59], 84: [2, 59], 85: [2, 59] }, { 33: [2, 61], 75: [2, 61] }, { 33: [2, 68], 37: 121, 74: 122, 75: [1, 120] }, { 33: [2, 65], 65: [2, 65], 72: [2, 65], 75: [2, 65], 80: [2, 65], 81: [2, 65], 82: [2, 65], 83: [2, 65], 84: [2, 65], 85: [2, 65] }, { 33: [2, 67], 75: [2, 67] }, { 23: [1, 123] }, { 23: [2, 51], 65: [2, 51], 72: [2, 51], 80: [2, 51], 81: [2, 51], 82: [2, 51], 83: [2, 51], 84: [2, 51], 85: [2, 51] }, { 23: [2, 53] }, { 33: [1, 124] }, { 33: [2, 91], 65: [2, 91], 72: [2, 91], 80: [2, 91], 81: [2, 91], 82: [2, 91], 83: [2, 91], 84: [2, 91], 85: [2, 91] }, { 33: [2, 93] }, { 5: [2, 22], 14: [2, 22], 15: [2, 22], 19: [2, 22], 29: [2, 22], 34: [2, 22], 39: [2, 22], 44: [2, 22], 47: [2, 22], 48: [2, 22], 51: [2, 22], 55: [2, 22], 60: [2, 22] }, { 23: [2, 99], 33: [2, 99], 54: [2, 99], 68: [2, 99], 72: [2, 99], 75: [2, 99] }, { 73: [1, 108] }, { 20: 74, 63: 125, 64: 75, 65: [1, 43], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 5: [2, 23], 14: [2, 23], 15: [2, 23], 19: [2, 23], 29: [2, 23], 34: [2, 23], 39: [2, 23], 44: [2, 23], 47: [2, 23], 48: [2, 23], 51: [2, 23], 55: [2, 23], 60: [2, 23] }, { 47: [2, 19] }, { 47: [2, 77] }, { 20: 74, 33: [2, 72], 41: 126, 63: 127, 64: 75, 65: [1, 43], 69: 128, 70: 76, 71: 77, 72: [1, 78], 75: [2, 72], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 5: [2, 24], 14: [2, 24], 15: [2, 24], 19: [2, 24], 29: [2, 24], 34: [2, 24], 39: [2, 24], 44: [2, 24], 47: [2, 24], 48: [2, 24], 51: [2, 24], 55: [2, 24], 60: [2, 24] }, { 68: [1, 129] }, { 65: [2, 95], 68: [2, 95], 72: [2, 95], 80: [2, 95], 81: [2, 95], 82: [2, 95], 83: [2, 95], 84: [2, 95], 85: [2, 95] }, { 68: [2, 97] }, { 5: [2, 21], 14: [2, 21], 15: [2, 21], 19: [2, 21], 29: [2, 21], 34: [2, 21], 39: [2, 21], 44: [2, 21], 47: [2, 21], 48: [2, 21], 51: [2, 21], 55: [2, 21], 60: [2, 21] }, { 33: [1, 130] }, { 33: [2, 63] }, { 72: [1, 132], 76: 131 }, { 33: [1, 133] }, { 33: [2, 69] }, { 15: [2, 12], 18: [2, 12] }, { 14: [2, 26], 15: [2, 26], 19: [2, 26], 29: [2, 26], 34: [2, 26], 47: [2, 26], 48: [2, 26], 51: [2, 26], 55: [2, 26], 60: [2, 26] }, { 23: [2, 31], 33: [2, 31], 54: [2, 31], 68: [2, 31], 72: [2, 31], 75: [2, 31] }, { 33: [2, 74], 42: 134, 74: 135, 75: [1, 120] }, { 33: [2, 71], 65: [2, 71], 72: [2, 71], 75: [2, 71], 80: [2, 71], 81: [2, 71], 82: [2, 71], 83: [2, 71], 84: [2, 71], 85: [2, 71] }, { 33: [2, 73], 75: [2, 73] }, { 23: [2, 29], 33: [2, 29], 54: [2, 29], 65: [2, 29], 68: [2, 29], 72: [2, 29], 75: [2, 29], 80: [2, 29], 81: [2, 29], 82: [2, 29], 83: [2, 29], 84: [2, 29], 85: [2, 29] }, { 14: [2, 15], 15: [2, 15], 19: [2, 15], 29: [2, 15], 34: [2, 15], 39: [2, 15], 44: [2, 15], 47: [2, 15], 48: [2, 15], 51: [2, 15], 55: [2, 15], 60: [2, 15] }, { 72: [1, 137], 77: [1, 136] }, { 72: [2, 100], 77: [2, 100] }, { 14: [2, 16], 15: [2, 16], 19: [2, 16], 29: [2, 16], 34: [2, 16], 44: [2, 16], 47: [2, 16], 48: [2, 16], 51: [2, 16], 55: [2, 16], 60: [2, 16] }, { 33: [1, 138] }, { 33: [2, 75] }, { 33: [2, 32] }, { 72: [2, 101], 77: [2, 101] }, { 14: [2, 17], 15: [2, 17], 19: [2, 17], 29: [2, 17], 34: [2, 17], 39: [2, 17], 44: [2, 17], 47: [2, 17], 48: [2, 17], 51: [2, 17], 55: [2, 17], 60: [2, 17] }], + defaultActions: { 4: [2, 1], 54: [2, 55], 56: [2, 20], 60: [2, 57], 73: [2, 81], 82: [2, 85], 86: [2, 18], 90: [2, 89], 101: [2, 53], 104: [2, 93], 110: [2, 19], 111: [2, 77], 116: [2, 97], 119: [2, 63], 122: [2, 69], 135: [2, 75], 136: [2, 32] }, + parseError: function parseError(str, hash) { + throw new Error(str); + }, + parse: function parse(input) { + var self = this, + stack = [0], + vstack = [null], + lstack = [], + table = this.table, + yytext = "", + yylineno = 0, + yyleng = 0, + recovering = 0, + TERROR = 2, + EOF = 1; + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + this.yy.parser = this; + if (typeof this.lexer.yylloc == "undefined") this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + var ranges = this.lexer.options && this.lexer.options.ranges; + if (typeof this.yy.parseError === "function") this.parseError = this.yy.parseError; + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + function lex() { + var token; + token = self.lexer.lex() || 1; + if (typeof token !== "number") { + token = self.symbols_[token] || token; + } + return token; + } + var symbol, + preErrorSymbol, + state, + action, + a, + r, + yyval = {}, + p, + len, + newState, + expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol === null || typeof symbol == "undefined") { + symbol = lex(); + } + action = table[state] && table[state][symbol]; + } + if (typeof action === "undefined" || !action.length || !action[0]) { + var errStr = ""; + if (!recovering) { + expected = []; + for (p in table[state]) if (this.terminals_[p] && p > 2) { + expected.push("'" + this.terminals_[p] + "'"); + } + if (this.lexer.showPosition) { + errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; + } else { + errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1 ? "end of input" : "'" + (this.terminals_[symbol] || symbol) + "'"); + } + this.parseError(errStr, { text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected }); + } + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) recovering--; + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = { first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column }; + if (ranges) { + yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; + } + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + if (typeof r !== "undefined") { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; + } + }; + /* Jison generated lexer */ + var lexer = (function () { + var lexer = { EOF: 1, + parseError: function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, + setInput: function setInput(input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = { first_line: 1, first_column: 0, last_line: 1, last_column: 0 }; + if (this.options.ranges) this.yylloc.range = [0, 0]; + this.offset = 0; + return this; + }, + input: function input() { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) this.yylloc.range[1]++; + + this._input = this._input.slice(1); + return ch; + }, + unput: function unput(ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length - len - 1); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length - 1); + this.matched = this.matched.substr(0, this.matched.length - 1); + + if (lines.length - 1) this.yylineno -= lines.length - 1; + var r = this.yylloc.range; + + this.yylloc = { first_line: this.yylloc.first_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.first_column, + last_column: lines ? (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length : this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + return this; + }, + more: function more() { + this._more = true; + return this; + }, + less: function less(n) { + this.unput(this.match.slice(n)); + }, + pastInput: function pastInput() { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, ""); + }, + upcomingInput: function upcomingInput() { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20 - next.length); + } + return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, ""); + }, + showPosition: function showPosition() { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c + "^"; + }, + next: function next() { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, match, tempMatch, index, col, lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i = 0; i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (!this.options.flex) break; + } + } + if (match) { + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = { first_line: this.yylloc.last_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length }; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[index], this.conditionStack[this.conditionStack.length - 1]); + if (this.done && this._input) this.done = false; + if (token) return token;else return; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), { text: "", token: null, line: this.yylineno }); + } + }, + lex: function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, + begin: function begin(condition) { + this.conditionStack.push(condition); + }, + popState: function popState() { + return this.conditionStack.pop(); + }, + _currentRules: function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules; + }, + topState: function topState() { + return this.conditionStack[this.conditionStack.length - 2]; + }, + pushState: function begin(condition) { + this.begin(condition); + } }; + lexer.options = {}; + lexer.performAction = function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) { + + function strip(start, end) { + return yy_.yytext = yy_.yytext.substring(start, yy_.yyleng - end + start); + } + + var YYSTATE = YY_START; + switch ($avoiding_name_collisions) { + case 0: + if (yy_.yytext.slice(-2) === "\\\\") { + strip(0, 1); + this.begin("mu"); + } else if (yy_.yytext.slice(-1) === "\\") { + strip(0, 1); + this.begin("emu"); + } else { + this.begin("mu"); + } + if (yy_.yytext) return 15; + + break; + case 1: + return 15; + break; + case 2: + this.popState(); + return 15; + + break; + case 3: + this.begin('raw');return 15; + break; + case 4: + this.popState(); + // Should be using `this.topState()` below, but it currently + // returns the second top instead of the first top. Opened an + // issue about it at https://github.com/zaach/jison/issues/291 + if (this.conditionStack[this.conditionStack.length - 1] === 'raw') { + return 15; + } else { + strip(5, 9); + return 'END_RAW_BLOCK'; + } + + break; + case 5: + return 15; + break; + case 6: + this.popState(); + return 14; + + break; + case 7: + return 65; + break; + case 8: + return 68; + break; + case 9: + return 19; + break; + case 10: + this.popState(); + this.begin('raw'); + return 23; + + break; + case 11: + return 55; + break; + case 12: + return 60; + break; + case 13: + return 29; + break; + case 14: + return 47; + break; + case 15: + this.popState();return 44; + break; + case 16: + this.popState();return 44; + break; + case 17: + return 34; + break; + case 18: + return 39; + break; + case 19: + return 51; + break; + case 20: + return 48; + break; + case 21: + this.unput(yy_.yytext); + this.popState(); + this.begin('com'); + + break; + case 22: + this.popState(); + return 14; + + break; + case 23: + return 48; + break; + case 24: + return 73; + break; + case 25: + return 72; + break; + case 26: + return 72; + break; + case 27: + return 87; + break; + case 28: + // ignore whitespace + break; + case 29: + this.popState();return 54; + break; + case 30: + this.popState();return 33; + break; + case 31: + yy_.yytext = strip(1, 2).replace(/\\"/g, '"');return 80; + break; + case 32: + yy_.yytext = strip(1, 2).replace(/\\'/g, "'");return 80; + break; + case 33: + return 85; + break; + case 34: + return 82; + break; + case 35: + return 82; + break; + case 36: + return 83; + break; + case 37: + return 84; + break; + case 38: + return 81; + break; + case 39: + return 75; + break; + case 40: + return 77; + break; + case 41: + return 72; + break; + case 42: + yy_.yytext = yy_.yytext.replace(/\\([\\\]])/g, '$1');return 72; + break; + case 43: + return 'INVALID'; + break; + case 44: + return 5; + break; + } + }; + lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/, /^(?:[^\x00]+)/, /^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/, /^(?:\{\{\{\{(?=[^\/]))/, /^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/, /^(?:[^\x00]+?(?=(\{\{\{\{)))/, /^(?:[\s\S]*?--(~)?\}\})/, /^(?:\()/, /^(?:\))/, /^(?:\{\{\{\{)/, /^(?:\}\}\}\})/, /^(?:\{\{(~)?>)/, /^(?:\{\{(~)?#>)/, /^(?:\{\{(~)?#\*?)/, /^(?:\{\{(~)?\/)/, /^(?:\{\{(~)?\^\s*(~)?\}\})/, /^(?:\{\{(~)?\s*else\s*(~)?\}\})/, /^(?:\{\{(~)?\^)/, /^(?:\{\{(~)?\s*else\b)/, /^(?:\{\{(~)?\{)/, /^(?:\{\{(~)?&)/, /^(?:\{\{(~)?!--)/, /^(?:\{\{(~)?![\s\S]*?\}\})/, /^(?:\{\{(~)?\*?)/, /^(?:=)/, /^(?:\.\.)/, /^(?:\.(?=([=~}\s\/.)|])))/, /^(?:[\/.])/, /^(?:\s+)/, /^(?:\}(~)?\}\})/, /^(?:(~)?\}\})/, /^(?:"(\\["]|[^"])*")/, /^(?:'(\\[']|[^'])*')/, /^(?:@)/, /^(?:true(?=([~}\s)])))/, /^(?:false(?=([~}\s)])))/, /^(?:undefined(?=([~}\s)])))/, /^(?:null(?=([~}\s)])))/, /^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/, /^(?:as\s+\|)/, /^(?:\|)/, /^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/, /^(?:\[(\\\]|[^\]])*\])/, /^(?:.)/, /^(?:$)/]; + lexer.conditions = { "mu": { "rules": [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44], "inclusive": false }, "emu": { "rules": [2], "inclusive": false }, "com": { "rules": [6], "inclusive": false }, "raw": { "rules": [3, 4, 5], "inclusive": false }, "INITIAL": { "rules": [0, 1, 44], "inclusive": true } }; + return lexer; + })(); + parser.lexer = lexer; + function Parser() { + this.yy = {}; + }Parser.prototype = parser;parser.Parser = Parser; + return new Parser(); + })();exports["default"] = handlebars; + module.exports = exports["default"]; + +/***/ }), +/* 48 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _visitor = __webpack_require__(49); + + var _visitor2 = _interopRequireDefault(_visitor); + + function WhitespaceControl() { + var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + this.options = options; + } + WhitespaceControl.prototype = new _visitor2['default'](); + + WhitespaceControl.prototype.Program = function (program) { + var doStandalone = !this.options.ignoreStandalone; + + var isRoot = !this.isRootSeen; + this.isRootSeen = true; + + var body = program.body; + for (var i = 0, l = body.length; i < l; i++) { + var current = body[i], + strip = this.accept(current); + + if (!strip) { + continue; + } + + var _isPrevWhitespace = isPrevWhitespace(body, i, isRoot), + _isNextWhitespace = isNextWhitespace(body, i, isRoot), + openStandalone = strip.openStandalone && _isPrevWhitespace, + closeStandalone = strip.closeStandalone && _isNextWhitespace, + inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace; + + if (strip.close) { + omitRight(body, i, true); + } + if (strip.open) { + omitLeft(body, i, true); + } + + if (doStandalone && inlineStandalone) { + omitRight(body, i); + + if (omitLeft(body, i)) { + // If we are on a standalone node, save the indent info for partials + if (current.type === 'PartialStatement') { + // Pull out the whitespace from the final line + current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1]; + } + } + } + if (doStandalone && openStandalone) { + omitRight((current.program || current.inverse).body); + + // Strip out the previous content node if it's whitespace only + omitLeft(body, i); + } + if (doStandalone && closeStandalone) { + // Always strip the next node + omitRight(body, i); + + omitLeft((current.inverse || current.program).body); + } + } + + return program; + }; + + WhitespaceControl.prototype.BlockStatement = WhitespaceControl.prototype.DecoratorBlock = WhitespaceControl.prototype.PartialBlockStatement = function (block) { + this.accept(block.program); + this.accept(block.inverse); + + // Find the inverse program that is involed with whitespace stripping. + var program = block.program || block.inverse, + inverse = block.program && block.inverse, + firstInverse = inverse, + lastInverse = inverse; + + if (inverse && inverse.chained) { + firstInverse = inverse.body[0].program; + + // Walk the inverse chain to find the last inverse that is actually in the chain. + while (lastInverse.chained) { + lastInverse = lastInverse.body[lastInverse.body.length - 1].program; + } + } + + var strip = { + open: block.openStrip.open, + close: block.closeStrip.close, + + // Determine the standalone candiacy. Basically flag our content as being possibly standalone + // so our parent can determine if we actually are standalone + openStandalone: isNextWhitespace(program.body), + closeStandalone: isPrevWhitespace((firstInverse || program).body) + }; + + if (block.openStrip.close) { + omitRight(program.body, null, true); + } + + if (inverse) { + var inverseStrip = block.inverseStrip; + + if (inverseStrip.open) { + omitLeft(program.body, null, true); + } + + if (inverseStrip.close) { + omitRight(firstInverse.body, null, true); + } + if (block.closeStrip.open) { + omitLeft(lastInverse.body, null, true); + } + + // Find standalone else statments + if (!this.options.ignoreStandalone && isPrevWhitespace(program.body) && isNextWhitespace(firstInverse.body)) { + omitLeft(program.body); + omitRight(firstInverse.body); + } + } else if (block.closeStrip.open) { + omitLeft(program.body, null, true); + } + + return strip; + }; + + WhitespaceControl.prototype.Decorator = WhitespaceControl.prototype.MustacheStatement = function (mustache) { + return mustache.strip; + }; + + WhitespaceControl.prototype.PartialStatement = WhitespaceControl.prototype.CommentStatement = function (node) { + /* istanbul ignore next */ + var strip = node.strip || {}; + return { + inlineStandalone: true, + open: strip.open, + close: strip.close + }; + }; + + function isPrevWhitespace(body, i, isRoot) { + if (i === undefined) { + i = body.length; + } + + // Nodes that end with newlines are considered whitespace (but are special + // cased for strip operations) + var prev = body[i - 1], + sibling = body[i - 2]; + if (!prev) { + return isRoot; + } + + if (prev.type === 'ContentStatement') { + return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(prev.original); + } + } + function isNextWhitespace(body, i, isRoot) { + if (i === undefined) { + i = -1; + } + + var next = body[i + 1], + sibling = body[i + 2]; + if (!next) { + return isRoot; + } + + if (next.type === 'ContentStatement') { + return (sibling || !isRoot ? /^\s*?\r?\n/ : /^\s*?(\r?\n|$)/).test(next.original); + } + } + + // Marks the node to the right of the position as omitted. + // I.e. {{foo}}' ' will mark the ' ' node as omitted. + // + // If i is undefined, then the first child will be marked as such. + // + // If mulitple is truthy then all whitespace will be stripped out until non-whitespace + // content is met. + function omitRight(body, i, multiple) { + var current = body[i == null ? 0 : i + 1]; + if (!current || current.type !== 'ContentStatement' || !multiple && current.rightStripped) { + return; + } + + var original = current.value; + current.value = current.value.replace(multiple ? /^\s+/ : /^[ \t]*\r?\n?/, ''); + current.rightStripped = current.value !== original; + } + + // Marks the node to the left of the position as omitted. + // I.e. ' '{{foo}} will mark the ' ' node as omitted. + // + // If i is undefined then the last child will be marked as such. + // + // If mulitple is truthy then all whitespace will be stripped out until non-whitespace + // content is met. + function omitLeft(body, i, multiple) { + var current = body[i == null ? body.length - 1 : i - 1]; + if (!current || current.type !== 'ContentStatement' || !multiple && current.leftStripped) { + return; + } + + // We omit the last node if it's whitespace only and not preceded by a non-content node. + var original = current.value; + current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, ''); + current.leftStripped = current.value !== original; + return current.leftStripped; + } + + exports['default'] = WhitespaceControl; + module.exports = exports['default']; + +/***/ }), +/* 49 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + function Visitor() { + this.parents = []; + } + + Visitor.prototype = { + constructor: Visitor, + mutating: false, + + // Visits a given value. If mutating, will replace the value if necessary. + acceptKey: function acceptKey(node, name) { + var value = this.accept(node[name]); + if (this.mutating) { + // Hacky sanity check: This may have a few false positives for type for the helper + // methods but will generally do the right thing without a lot of overhead. + if (value && !Visitor.prototype[value.type]) { + throw new _exception2['default']('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type); + } + node[name] = value; + } + }, + + // Performs an accept operation with added sanity check to ensure + // required keys are not removed. + acceptRequired: function acceptRequired(node, name) { + this.acceptKey(node, name); + + if (!node[name]) { + throw new _exception2['default'](node.type + ' requires ' + name); + } + }, + + // Traverses a given array. If mutating, empty respnses will be removed + // for child elements. + acceptArray: function acceptArray(array) { + for (var i = 0, l = array.length; i < l; i++) { + this.acceptKey(array, i); + + if (!array[i]) { + array.splice(i, 1); + i--; + l--; + } + } + }, + + accept: function accept(object) { + if (!object) { + return; + } + + /* istanbul ignore next: Sanity code */ + if (!this[object.type]) { + throw new _exception2['default']('Unknown type: ' + object.type, object); + } + + if (this.current) { + this.parents.unshift(this.current); + } + this.current = object; + + var ret = this[object.type](object); + + this.current = this.parents.shift(); + + if (!this.mutating || ret) { + return ret; + } else if (ret !== false) { + return object; + } + }, + + Program: function Program(program) { + this.acceptArray(program.body); + }, + + MustacheStatement: visitSubExpression, + Decorator: visitSubExpression, + + BlockStatement: visitBlock, + DecoratorBlock: visitBlock, + + PartialStatement: visitPartial, + PartialBlockStatement: function PartialBlockStatement(partial) { + visitPartial.call(this, partial); + + this.acceptKey(partial, 'program'); + }, + + ContentStatement: function ContentStatement() /* content */{}, + CommentStatement: function CommentStatement() /* comment */{}, + + SubExpression: visitSubExpression, + + PathExpression: function PathExpression() /* path */{}, + + StringLiteral: function StringLiteral() /* string */{}, + NumberLiteral: function NumberLiteral() /* number */{}, + BooleanLiteral: function BooleanLiteral() /* bool */{}, + UndefinedLiteral: function UndefinedLiteral() /* literal */{}, + NullLiteral: function NullLiteral() /* literal */{}, + + Hash: function Hash(hash) { + this.acceptArray(hash.pairs); + }, + HashPair: function HashPair(pair) { + this.acceptRequired(pair, 'value'); + } + }; + + function visitSubExpression(mustache) { + this.acceptRequired(mustache, 'path'); + this.acceptArray(mustache.params); + this.acceptKey(mustache, 'hash'); + } + function visitBlock(block) { + visitSubExpression.call(this, block); + + this.acceptKey(block, 'program'); + this.acceptKey(block, 'inverse'); + } + function visitPartial(partial) { + this.acceptRequired(partial, 'name'); + this.acceptArray(partial.params); + this.acceptKey(partial, 'hash'); + } + + exports['default'] = Visitor; + module.exports = exports['default']; + +/***/ }), +/* 50 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.SourceLocation = SourceLocation; + exports.id = id; + exports.stripFlags = stripFlags; + exports.stripComment = stripComment; + exports.preparePath = preparePath; + exports.prepareMustache = prepareMustache; + exports.prepareRawBlock = prepareRawBlock; + exports.prepareBlock = prepareBlock; + exports.prepareProgram = prepareProgram; + exports.preparePartialBlock = preparePartialBlock; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + function validateClose(open, close) { + close = close.path ? close.path.original : close; + + if (open.path.original !== close) { + var errorNode = { loc: open.path.loc }; + + throw new _exception2['default'](open.path.original + " doesn't match " + close, errorNode); + } + } + + function SourceLocation(source, locInfo) { + this.source = source; + this.start = { + line: locInfo.first_line, + column: locInfo.first_column + }; + this.end = { + line: locInfo.last_line, + column: locInfo.last_column + }; + } + + function id(token) { + if (/^\[.*\]$/.test(token)) { + return token.substring(1, token.length - 1); + } else { + return token; + } + } + + function stripFlags(open, close) { + return { + open: open.charAt(2) === '~', + close: close.charAt(close.length - 3) === '~' + }; + } + + function stripComment(comment) { + return comment.replace(/^\{\{~?!-?-?/, '').replace(/-?-?~?\}\}$/, ''); + } + + function preparePath(data, parts, loc) { + loc = this.locInfo(loc); + + var original = data ? '@' : '', + dig = [], + depth = 0; + + for (var i = 0, l = parts.length; i < l; i++) { + var part = parts[i].part, + + // If we have [] syntax then we do not treat path references as operators, + // i.e. foo.[this] resolves to approximately context.foo['this'] + isLiteral = parts[i].original !== part; + original += (parts[i].separator || '') + part; + + if (!isLiteral && (part === '..' || part === '.' || part === 'this')) { + if (dig.length > 0) { + throw new _exception2['default']('Invalid path: ' + original, { loc: loc }); + } else if (part === '..') { + depth++; + } + } else { + dig.push(part); + } + } + + return { + type: 'PathExpression', + data: data, + depth: depth, + parts: dig, + original: original, + loc: loc + }; + } + + function prepareMustache(path, params, hash, open, strip, locInfo) { + // Must use charAt to support IE pre-10 + var escapeFlag = open.charAt(3) || open.charAt(2), + escaped = escapeFlag !== '{' && escapeFlag !== '&'; + + var decorator = /\*/.test(open); + return { + type: decorator ? 'Decorator' : 'MustacheStatement', + path: path, + params: params, + hash: hash, + escaped: escaped, + strip: strip, + loc: this.locInfo(locInfo) + }; + } + + function prepareRawBlock(openRawBlock, contents, close, locInfo) { + validateClose(openRawBlock, close); + + locInfo = this.locInfo(locInfo); + var program = { + type: 'Program', + body: contents, + strip: {}, + loc: locInfo + }; + + return { + type: 'BlockStatement', + path: openRawBlock.path, + params: openRawBlock.params, + hash: openRawBlock.hash, + program: program, + openStrip: {}, + inverseStrip: {}, + closeStrip: {}, + loc: locInfo + }; + } + + function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) { + if (close && close.path) { + validateClose(openBlock, close); + } + + var decorator = /\*/.test(openBlock.open); + + program.blockParams = openBlock.blockParams; + + var inverse = undefined, + inverseStrip = undefined; + + if (inverseAndProgram) { + if (decorator) { + throw new _exception2['default']('Unexpected inverse block on decorator', inverseAndProgram); + } + + if (inverseAndProgram.chain) { + inverseAndProgram.program.body[0].closeStrip = close.strip; + } + + inverseStrip = inverseAndProgram.strip; + inverse = inverseAndProgram.program; + } + + if (inverted) { + inverted = inverse; + inverse = program; + program = inverted; + } + + return { + type: decorator ? 'DecoratorBlock' : 'BlockStatement', + path: openBlock.path, + params: openBlock.params, + hash: openBlock.hash, + program: program, + inverse: inverse, + openStrip: openBlock.strip, + inverseStrip: inverseStrip, + closeStrip: close && close.strip, + loc: this.locInfo(locInfo) + }; + } + + function prepareProgram(statements, loc) { + if (!loc && statements.length) { + var firstLoc = statements[0].loc, + lastLoc = statements[statements.length - 1].loc; + + /* istanbul ignore else */ + if (firstLoc && lastLoc) { + loc = { + source: firstLoc.source, + start: { + line: firstLoc.start.line, + column: firstLoc.start.column + }, + end: { + line: lastLoc.end.line, + column: lastLoc.end.column + } + }; + } + } + + return { + type: 'Program', + body: statements, + strip: {}, + loc: loc + }; + } + + function preparePartialBlock(open, program, close, locInfo) { + validateClose(open, close); + + return { + type: 'PartialBlockStatement', + name: open.path, + params: open.params, + hash: open.hash, + program: program, + openStrip: open.strip, + closeStrip: close && close.strip, + loc: this.locInfo(locInfo) + }; + } + +/***/ }), +/* 51 */ +/***/ (function(module, exports, __webpack_require__) { + + /* eslint-disable new-cap */ + + 'use strict'; + + var _Object$create = __webpack_require__(34)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.Compiler = Compiler; + exports.precompile = precompile; + exports.compile = compile; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _utils = __webpack_require__(5); + + var _ast = __webpack_require__(45); + + var _ast2 = _interopRequireDefault(_ast); + + var slice = [].slice; + + function Compiler() {} + + // the foundHelper register will disambiguate helper lookup from finding a + // function in a context. This is necessary for mustache compatibility, which + // requires that context functions in blocks are evaluated by blockHelperMissing, + // and then proceed as if the resulting value was provided to blockHelperMissing. + + Compiler.prototype = { + compiler: Compiler, + + equals: function equals(other) { + var len = this.opcodes.length; + if (other.opcodes.length !== len) { + return false; + } + + for (var i = 0; i < len; i++) { + var opcode = this.opcodes[i], + otherOpcode = other.opcodes[i]; + if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) { + return false; + } + } + + // We know that length is the same between the two arrays because they are directly tied + // to the opcode behavior above. + len = this.children.length; + for (var i = 0; i < len; i++) { + if (!this.children[i].equals(other.children[i])) { + return false; + } + } + + return true; + }, + + guid: 0, + + compile: function compile(program, options) { + this.sourceNode = []; + this.opcodes = []; + this.children = []; + this.options = options; + this.stringParams = options.stringParams; + this.trackIds = options.trackIds; + + options.blockParams = options.blockParams || []; + + options.knownHelpers = _utils.extend(_Object$create(null), { + helperMissing: true, + blockHelperMissing: true, + each: true, + 'if': true, + unless: true, + 'with': true, + log: true, + lookup: true + }, options.knownHelpers); + + return this.accept(program); + }, + + compileProgram: function compileProgram(program) { + var childCompiler = new this.compiler(), + // eslint-disable-line new-cap + result = childCompiler.compile(program, this.options), + guid = this.guid++; + + this.usePartial = this.usePartial || result.usePartial; + + this.children[guid] = result; + this.useDepths = this.useDepths || result.useDepths; + + return guid; + }, + + accept: function accept(node) { + /* istanbul ignore next: Sanity code */ + if (!this[node.type]) { + throw new _exception2['default']('Unknown type: ' + node.type, node); + } + + this.sourceNode.unshift(node); + var ret = this[node.type](node); + this.sourceNode.shift(); + return ret; + }, + + Program: function Program(program) { + this.options.blockParams.unshift(program.blockParams); + + var body = program.body, + bodyLength = body.length; + for (var i = 0; i < bodyLength; i++) { + this.accept(body[i]); + } + + this.options.blockParams.shift(); + + this.isSimple = bodyLength === 1; + this.blockParams = program.blockParams ? program.blockParams.length : 0; + + return this; + }, + + BlockStatement: function BlockStatement(block) { + transformLiteralToPath(block); + + var program = block.program, + inverse = block.inverse; + + program = program && this.compileProgram(program); + inverse = inverse && this.compileProgram(inverse); + + var type = this.classifySexpr(block); + + if (type === 'helper') { + this.helperSexpr(block, program, inverse); + } else if (type === 'simple') { + this.simpleSexpr(block); + + // now that the simple mustache is resolved, we need to + // evaluate it by executing `blockHelperMissing` + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + this.opcode('emptyHash'); + this.opcode('blockValue', block.path.original); + } else { + this.ambiguousSexpr(block, program, inverse); + + // now that the simple mustache is resolved, we need to + // evaluate it by executing `blockHelperMissing` + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + this.opcode('emptyHash'); + this.opcode('ambiguousBlockValue'); + } + + this.opcode('append'); + }, + + DecoratorBlock: function DecoratorBlock(decorator) { + var program = decorator.program && this.compileProgram(decorator.program); + var params = this.setupFullMustacheParams(decorator, program, undefined), + path = decorator.path; + + this.useDecorators = true; + this.opcode('registerDecorator', params.length, path.original); + }, + + PartialStatement: function PartialStatement(partial) { + this.usePartial = true; + + var program = partial.program; + if (program) { + program = this.compileProgram(partial.program); + } + + var params = partial.params; + if (params.length > 1) { + throw new _exception2['default']('Unsupported number of partial arguments: ' + params.length, partial); + } else if (!params.length) { + if (this.options.explicitPartialContext) { + this.opcode('pushLiteral', 'undefined'); + } else { + params.push({ type: 'PathExpression', parts: [], depth: 0 }); + } + } + + var partialName = partial.name.original, + isDynamic = partial.name.type === 'SubExpression'; + if (isDynamic) { + this.accept(partial.name); + } + + this.setupFullMustacheParams(partial, program, undefined, true); + + var indent = partial.indent || ''; + if (this.options.preventIndent && indent) { + this.opcode('appendContent', indent); + indent = ''; + } + + this.opcode('invokePartial', isDynamic, partialName, indent); + this.opcode('append'); + }, + PartialBlockStatement: function PartialBlockStatement(partialBlock) { + this.PartialStatement(partialBlock); + }, + + MustacheStatement: function MustacheStatement(mustache) { + this.SubExpression(mustache); + + if (mustache.escaped && !this.options.noEscape) { + this.opcode('appendEscaped'); + } else { + this.opcode('append'); + } + }, + Decorator: function Decorator(decorator) { + this.DecoratorBlock(decorator); + }, + + ContentStatement: function ContentStatement(content) { + if (content.value) { + this.opcode('appendContent', content.value); + } + }, + + CommentStatement: function CommentStatement() {}, + + SubExpression: function SubExpression(sexpr) { + transformLiteralToPath(sexpr); + var type = this.classifySexpr(sexpr); + + if (type === 'simple') { + this.simpleSexpr(sexpr); + } else if (type === 'helper') { + this.helperSexpr(sexpr); + } else { + this.ambiguousSexpr(sexpr); + } + }, + ambiguousSexpr: function ambiguousSexpr(sexpr, program, inverse) { + var path = sexpr.path, + name = path.parts[0], + isBlock = program != null || inverse != null; + + this.opcode('getContext', path.depth); + + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + + path.strict = true; + this.accept(path); + + this.opcode('invokeAmbiguous', name, isBlock); + }, + + simpleSexpr: function simpleSexpr(sexpr) { + var path = sexpr.path; + path.strict = true; + this.accept(path); + this.opcode('resolvePossibleLambda'); + }, + + helperSexpr: function helperSexpr(sexpr, program, inverse) { + var params = this.setupFullMustacheParams(sexpr, program, inverse), + path = sexpr.path, + name = path.parts[0]; + + if (this.options.knownHelpers[name]) { + this.opcode('invokeKnownHelper', params.length, name); + } else if (this.options.knownHelpersOnly) { + throw new _exception2['default']('You specified knownHelpersOnly, but used the unknown helper ' + name, sexpr); + } else { + path.strict = true; + path.falsy = true; + + this.accept(path); + this.opcode('invokeHelper', params.length, path.original, _ast2['default'].helpers.simpleId(path)); + } + }, + + PathExpression: function PathExpression(path) { + this.addDepth(path.depth); + this.opcode('getContext', path.depth); + + var name = path.parts[0], + scoped = _ast2['default'].helpers.scopedId(path), + blockParamId = !path.depth && !scoped && this.blockParamIndex(name); + + if (blockParamId) { + this.opcode('lookupBlockParam', blockParamId, path.parts); + } else if (!name) { + // Context reference, i.e. `{{foo .}}` or `{{foo ..}}` + this.opcode('pushContext'); + } else if (path.data) { + this.options.data = true; + this.opcode('lookupData', path.depth, path.parts, path.strict); + } else { + this.opcode('lookupOnContext', path.parts, path.falsy, path.strict, scoped); + } + }, + + StringLiteral: function StringLiteral(string) { + this.opcode('pushString', string.value); + }, + + NumberLiteral: function NumberLiteral(number) { + this.opcode('pushLiteral', number.value); + }, + + BooleanLiteral: function BooleanLiteral(bool) { + this.opcode('pushLiteral', bool.value); + }, + + UndefinedLiteral: function UndefinedLiteral() { + this.opcode('pushLiteral', 'undefined'); + }, + + NullLiteral: function NullLiteral() { + this.opcode('pushLiteral', 'null'); + }, + + Hash: function Hash(hash) { + var pairs = hash.pairs, + i = 0, + l = pairs.length; + + this.opcode('pushHash'); + + for (; i < l; i++) { + this.pushParam(pairs[i].value); + } + while (i--) { + this.opcode('assignToHash', pairs[i].key); + } + this.opcode('popHash'); + }, + + // HELPERS + opcode: function opcode(name) { + this.opcodes.push({ + opcode: name, + args: slice.call(arguments, 1), + loc: this.sourceNode[0].loc + }); + }, + + addDepth: function addDepth(depth) { + if (!depth) { + return; + } + + this.useDepths = true; + }, + + classifySexpr: function classifySexpr(sexpr) { + var isSimple = _ast2['default'].helpers.simpleId(sexpr.path); + + var isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]); + + // a mustache is an eligible helper if: + // * its id is simple (a single part, not `this` or `..`) + var isHelper = !isBlockParam && _ast2['default'].helpers.helperExpression(sexpr); + + // if a mustache is an eligible helper but not a definite + // helper, it is ambiguous, and will be resolved in a later + // pass or at runtime. + var isEligible = !isBlockParam && (isHelper || isSimple); + + // if ambiguous, we can possibly resolve the ambiguity now + // An eligible helper is one that does not have a complex path, i.e. `this.foo`, `../foo` etc. + if (isEligible && !isHelper) { + var _name = sexpr.path.parts[0], + options = this.options; + if (options.knownHelpers[_name]) { + isHelper = true; + } else if (options.knownHelpersOnly) { + isEligible = false; + } + } + + if (isHelper) { + return 'helper'; + } else if (isEligible) { + return 'ambiguous'; + } else { + return 'simple'; + } + }, + + pushParams: function pushParams(params) { + for (var i = 0, l = params.length; i < l; i++) { + this.pushParam(params[i]); + } + }, + + pushParam: function pushParam(val) { + var value = val.value != null ? val.value : val.original || ''; + + if (this.stringParams) { + if (value.replace) { + value = value.replace(/^(\.?\.\/)*/g, '').replace(/\//g, '.'); + } + + if (val.depth) { + this.addDepth(val.depth); + } + this.opcode('getContext', val.depth || 0); + this.opcode('pushStringParam', value, val.type); + + if (val.type === 'SubExpression') { + // SubExpressions get evaluated and passed in + // in string params mode. + this.accept(val); + } + } else { + if (this.trackIds) { + var blockParamIndex = undefined; + if (val.parts && !_ast2['default'].helpers.scopedId(val) && !val.depth) { + blockParamIndex = this.blockParamIndex(val.parts[0]); + } + if (blockParamIndex) { + var blockParamChild = val.parts.slice(1).join('.'); + this.opcode('pushId', 'BlockParam', blockParamIndex, blockParamChild); + } else { + value = val.original || value; + if (value.replace) { + value = value.replace(/^this(?:\.|$)/, '').replace(/^\.\//, '').replace(/^\.$/, ''); + } + + this.opcode('pushId', val.type, value); + } + } + this.accept(val); + } + }, + + setupFullMustacheParams: function setupFullMustacheParams(sexpr, program, inverse, omitEmpty) { + var params = sexpr.params; + this.pushParams(params); + + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + + if (sexpr.hash) { + this.accept(sexpr.hash); + } else { + this.opcode('emptyHash', omitEmpty); + } + + return params; + }, + + blockParamIndex: function blockParamIndex(name) { + for (var depth = 0, len = this.options.blockParams.length; depth < len; depth++) { + var blockParams = this.options.blockParams[depth], + param = blockParams && _utils.indexOf(blockParams, name); + if (blockParams && param >= 0) { + return [depth, param]; + } + } + } + }; + + function precompile(input, options, env) { + if (input == null || typeof input !== 'string' && input.type !== 'Program') { + throw new _exception2['default']('You must pass a string or Handlebars AST to Handlebars.precompile. You passed ' + input); + } + + options = options || {}; + if (!('data' in options)) { + options.data = true; + } + if (options.compat) { + options.useDepths = true; + } + + var ast = env.parse(input, options), + environment = new env.Compiler().compile(ast, options); + return new env.JavaScriptCompiler().compile(environment, options); + } + + function compile(input, options, env) { + if (options === undefined) options = {}; + + if (input == null || typeof input !== 'string' && input.type !== 'Program') { + throw new _exception2['default']('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input); + } + + options = _utils.extend({}, options); + if (!('data' in options)) { + options.data = true; + } + if (options.compat) { + options.useDepths = true; + } + + var compiled = undefined; + + function compileInput() { + var ast = env.parse(input, options), + environment = new env.Compiler().compile(ast, options), + templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true); + return env.template(templateSpec); + } + + // Template is only compiled on first use and cached after that point. + function ret(context, execOptions) { + if (!compiled) { + compiled = compileInput(); + } + return compiled.call(this, context, execOptions); + } + ret._setup = function (setupOptions) { + if (!compiled) { + compiled = compileInput(); + } + return compiled._setup(setupOptions); + }; + ret._child = function (i, data, blockParams, depths) { + if (!compiled) { + compiled = compileInput(); + } + return compiled._child(i, data, blockParams, depths); + }; + return ret; + } + + function argEquals(a, b) { + if (a === b) { + return true; + } + + if (_utils.isArray(a) && _utils.isArray(b) && a.length === b.length) { + for (var i = 0; i < a.length; i++) { + if (!argEquals(a[i], b[i])) { + return false; + } + } + return true; + } + } + + function transformLiteralToPath(sexpr) { + if (!sexpr.path.parts) { + var literal = sexpr.path; + // Casting to string here to make false and 0 literal values play nicely with the rest + // of the system. + sexpr.path = { + type: 'PathExpression', + data: false, + depth: 0, + parts: [literal.original + ''], + original: literal.original + '', + loc: literal.loc + }; + } + } + +/***/ }), +/* 52 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var _Object$keys = __webpack_require__(13)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _base = __webpack_require__(4); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _utils = __webpack_require__(5); + + var _codeGen = __webpack_require__(53); + + var _codeGen2 = _interopRequireDefault(_codeGen); + + function Literal(value) { + this.value = value; + } + + function JavaScriptCompiler() {} + + JavaScriptCompiler.prototype = { + // PUBLIC API: You can override these methods in a subclass to provide + // alternative compiled forms for name lookup and buffering semantics + nameLookup: function nameLookup(parent, name /*, type */) { + return this.internalNameLookup(parent, name); + }, + depthedLookup: function depthedLookup(name) { + return [this.aliasable('container.lookup'), '(depths, ', JSON.stringify(name), ')']; + }, + + compilerInfo: function compilerInfo() { + var revision = _base.COMPILER_REVISION, + versions = _base.REVISION_CHANGES[revision]; + return [revision, versions]; + }, + + appendToBuffer: function appendToBuffer(source, location, explicit) { + // Force a source as this simplifies the merge logic. + if (!_utils.isArray(source)) { + source = [source]; + } + source = this.source.wrap(source, location); + + if (this.environment.isSimple) { + return ['return ', source, ';']; + } else if (explicit) { + // This is a case where the buffer operation occurs as a child of another + // construct, generally braces. We have to explicitly output these buffer + // operations to ensure that the emitted code goes in the correct location. + return ['buffer += ', source, ';']; + } else { + source.appendToBuffer = true; + return source; + } + }, + + initializeBuffer: function initializeBuffer() { + return this.quotedString(''); + }, + // END PUBLIC API + internalNameLookup: function internalNameLookup(parent, name) { + this.lookupPropertyFunctionIsUsed = true; + return ['lookupProperty(', parent, ',', JSON.stringify(name), ')']; + }, + + lookupPropertyFunctionIsUsed: false, + + compile: function compile(environment, options, context, asObject) { + this.environment = environment; + this.options = options; + this.stringParams = this.options.stringParams; + this.trackIds = this.options.trackIds; + this.precompile = !asObject; + + this.name = this.environment.name; + this.isChild = !!context; + this.context = context || { + decorators: [], + programs: [], + environments: [] + }; + + this.preamble(); + + this.stackSlot = 0; + this.stackVars = []; + this.aliases = {}; + this.registers = { list: [] }; + this.hashes = []; + this.compileStack = []; + this.inlineStack = []; + this.blockParams = []; + + this.compileChildren(environment, options); + + this.useDepths = this.useDepths || environment.useDepths || environment.useDecorators || this.options.compat; + this.useBlockParams = this.useBlockParams || environment.useBlockParams; + + var opcodes = environment.opcodes, + opcode = undefined, + firstLoc = undefined, + i = undefined, + l = undefined; + + for (i = 0, l = opcodes.length; i < l; i++) { + opcode = opcodes[i]; + + this.source.currentLocation = opcode.loc; + firstLoc = firstLoc || opcode.loc; + this[opcode.opcode].apply(this, opcode.args); + } + + // Flush any trailing content that might be pending. + this.source.currentLocation = firstLoc; + this.pushSource(''); + + /* istanbul ignore next */ + if (this.stackSlot || this.inlineStack.length || this.compileStack.length) { + throw new _exception2['default']('Compile completed with content left on stack'); + } + + if (!this.decorators.isEmpty()) { + this.useDecorators = true; + + this.decorators.prepend(['var decorators = container.decorators, ', this.lookupPropertyFunctionVarDeclaration(), ';\n']); + this.decorators.push('return fn;'); + + if (asObject) { + this.decorators = Function.apply(this, ['fn', 'props', 'container', 'depth0', 'data', 'blockParams', 'depths', this.decorators.merge()]); + } else { + this.decorators.prepend('function(fn, props, container, depth0, data, blockParams, depths) {\n'); + this.decorators.push('}\n'); + this.decorators = this.decorators.merge(); + } + } else { + this.decorators = undefined; + } + + var fn = this.createFunctionContext(asObject); + if (!this.isChild) { + var ret = { + compiler: this.compilerInfo(), + main: fn + }; + + if (this.decorators) { + ret.main_d = this.decorators; // eslint-disable-line camelcase + ret.useDecorators = true; + } + + var _context = this.context; + var programs = _context.programs; + var decorators = _context.decorators; + + for (i = 0, l = programs.length; i < l; i++) { + if (programs[i]) { + ret[i] = programs[i]; + if (decorators[i]) { + ret[i + '_d'] = decorators[i]; + ret.useDecorators = true; + } + } + } + + if (this.environment.usePartial) { + ret.usePartial = true; + } + if (this.options.data) { + ret.useData = true; + } + if (this.useDepths) { + ret.useDepths = true; + } + if (this.useBlockParams) { + ret.useBlockParams = true; + } + if (this.options.compat) { + ret.compat = true; + } + + if (!asObject) { + ret.compiler = JSON.stringify(ret.compiler); + + this.source.currentLocation = { start: { line: 1, column: 0 } }; + ret = this.objectLiteral(ret); + + if (options.srcName) { + ret = ret.toStringWithSourceMap({ file: options.destName }); + ret.map = ret.map && ret.map.toString(); + } else { + ret = ret.toString(); + } + } else { + ret.compilerOptions = this.options; + } + + return ret; + } else { + return fn; + } + }, + + preamble: function preamble() { + // track the last context pushed into place to allow skipping the + // getContext opcode when it would be a noop + this.lastContext = 0; + this.source = new _codeGen2['default'](this.options.srcName); + this.decorators = new _codeGen2['default'](this.options.srcName); + }, + + createFunctionContext: function createFunctionContext(asObject) { + // istanbul ignore next + + var _this = this; + + var varDeclarations = ''; + + var locals = this.stackVars.concat(this.registers.list); + if (locals.length > 0) { + varDeclarations += ', ' + locals.join(', '); + } + + // Generate minimizer alias mappings + // + // When using true SourceNodes, this will update all references to the given alias + // as the source nodes are reused in situ. For the non-source node compilation mode, + // aliases will not be used, but this case is already being run on the client and + // we aren't concern about minimizing the template size. + var aliasCount = 0; + _Object$keys(this.aliases).forEach(function (alias) { + var node = _this.aliases[alias]; + if (node.children && node.referenceCount > 1) { + varDeclarations += ', alias' + ++aliasCount + '=' + alias; + node.children[0] = 'alias' + aliasCount; + } + }); + + if (this.lookupPropertyFunctionIsUsed) { + varDeclarations += ', ' + this.lookupPropertyFunctionVarDeclaration(); + } + + var params = ['container', 'depth0', 'helpers', 'partials', 'data']; + + if (this.useBlockParams || this.useDepths) { + params.push('blockParams'); + } + if (this.useDepths) { + params.push('depths'); + } + + // Perform a second pass over the output to merge content when possible + var source = this.mergeSource(varDeclarations); + + if (asObject) { + params.push(source); + + return Function.apply(this, params); + } else { + return this.source.wrap(['function(', params.join(','), ') {\n ', source, '}']); + } + }, + mergeSource: function mergeSource(varDeclarations) { + var isSimple = this.environment.isSimple, + appendOnly = !this.forceBuffer, + appendFirst = undefined, + sourceSeen = undefined, + bufferStart = undefined, + bufferEnd = undefined; + this.source.each(function (line) { + if (line.appendToBuffer) { + if (bufferStart) { + line.prepend(' + '); + } else { + bufferStart = line; + } + bufferEnd = line; + } else { + if (bufferStart) { + if (!sourceSeen) { + appendFirst = true; + } else { + bufferStart.prepend('buffer += '); + } + bufferEnd.add(';'); + bufferStart = bufferEnd = undefined; + } + + sourceSeen = true; + if (!isSimple) { + appendOnly = false; + } + } + }); + + if (appendOnly) { + if (bufferStart) { + bufferStart.prepend('return '); + bufferEnd.add(';'); + } else if (!sourceSeen) { + this.source.push('return "";'); + } + } else { + varDeclarations += ', buffer = ' + (appendFirst ? '' : this.initializeBuffer()); + + if (bufferStart) { + bufferStart.prepend('return buffer + '); + bufferEnd.add(';'); + } else { + this.source.push('return buffer;'); + } + } + + if (varDeclarations) { + this.source.prepend('var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n')); + } + + return this.source.merge(); + }, + + lookupPropertyFunctionVarDeclaration: function lookupPropertyFunctionVarDeclaration() { + return '\n lookupProperty = container.lookupProperty || function(parent, propertyName) {\n if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {\n return parent[propertyName];\n }\n return undefined\n }\n '.trim(); + }, + + // [blockValue] + // + // On stack, before: hash, inverse, program, value + // On stack, after: return value of blockHelperMissing + // + // The purpose of this opcode is to take a block of the form + // `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and + // replace it on the stack with the result of properly + // invoking blockHelperMissing. + blockValue: function blockValue(name) { + var blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'), + params = [this.contextName(0)]; + this.setupHelperArgs(name, 0, params); + + var blockName = this.popStack(); + params.splice(1, 0, blockName); + + this.push(this.source.functionCall(blockHelperMissing, 'call', params)); + }, + + // [ambiguousBlockValue] + // + // On stack, before: hash, inverse, program, value + // Compiler value, before: lastHelper=value of last found helper, if any + // On stack, after, if no lastHelper: same as [blockValue] + // On stack, after, if lastHelper: value + ambiguousBlockValue: function ambiguousBlockValue() { + // We're being a bit cheeky and reusing the options value from the prior exec + var blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'), + params = [this.contextName(0)]; + this.setupHelperArgs('', 0, params, true); + + this.flushInline(); + + var current = this.topStack(); + params.splice(1, 0, current); + + this.pushSource(['if (!', this.lastHelper, ') { ', current, ' = ', this.source.functionCall(blockHelperMissing, 'call', params), '}']); + }, + + // [appendContent] + // + // On stack, before: ... + // On stack, after: ... + // + // Appends the string value of `content` to the current buffer + appendContent: function appendContent(content) { + if (this.pendingContent) { + content = this.pendingContent + content; + } else { + this.pendingLocation = this.source.currentLocation; + } + + this.pendingContent = content; + }, + + // [append] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Coerces `value` to a String and appends it to the current buffer. + // + // If `value` is truthy, or 0, it is coerced into a string and appended + // Otherwise, the empty string is appended + append: function append() { + if (this.isInline()) { + this.replaceStack(function (current) { + return [' != null ? ', current, ' : ""']; + }); + + this.pushSource(this.appendToBuffer(this.popStack())); + } else { + var local = this.popStack(); + this.pushSource(['if (', local, ' != null) { ', this.appendToBuffer(local, undefined, true), ' }']); + if (this.environment.isSimple) { + this.pushSource(['else { ', this.appendToBuffer("''", undefined, true), ' }']); + } + } + }, + + // [appendEscaped] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Escape `value` and append it to the buffer + appendEscaped: function appendEscaped() { + this.pushSource(this.appendToBuffer([this.aliasable('container.escapeExpression'), '(', this.popStack(), ')'])); + }, + + // [getContext] + // + // On stack, before: ... + // On stack, after: ... + // Compiler value, after: lastContext=depth + // + // Set the value of the `lastContext` compiler value to the depth + getContext: function getContext(depth) { + this.lastContext = depth; + }, + + // [pushContext] + // + // On stack, before: ... + // On stack, after: currentContext, ... + // + // Pushes the value of the current context onto the stack. + pushContext: function pushContext() { + this.pushStackLiteral(this.contextName(this.lastContext)); + }, + + // [lookupOnContext] + // + // On stack, before: ... + // On stack, after: currentContext[name], ... + // + // Looks up the value of `name` on the current context and pushes + // it onto the stack. + lookupOnContext: function lookupOnContext(parts, falsy, strict, scoped) { + var i = 0; + + if (!scoped && this.options.compat && !this.lastContext) { + // The depthed query is expected to handle the undefined logic for the root level that + // is implemented below, so we evaluate that directly in compat mode + this.push(this.depthedLookup(parts[i++])); + } else { + this.pushContext(); + } + + this.resolvePath('context', parts, i, falsy, strict); + }, + + // [lookupBlockParam] + // + // On stack, before: ... + // On stack, after: blockParam[name], ... + // + // Looks up the value of `parts` on the given block param and pushes + // it onto the stack. + lookupBlockParam: function lookupBlockParam(blockParamId, parts) { + this.useBlockParams = true; + + this.push(['blockParams[', blockParamId[0], '][', blockParamId[1], ']']); + this.resolvePath('context', parts, 1); + }, + + // [lookupData] + // + // On stack, before: ... + // On stack, after: data, ... + // + // Push the data lookup operator + lookupData: function lookupData(depth, parts, strict) { + if (!depth) { + this.pushStackLiteral('data'); + } else { + this.pushStackLiteral('container.data(data, ' + depth + ')'); + } + + this.resolvePath('data', parts, 0, true, strict); + }, + + resolvePath: function resolvePath(type, parts, i, falsy, strict) { + // istanbul ignore next + + var _this2 = this; + + if (this.options.strict || this.options.assumeObjects) { + this.push(strictLookup(this.options.strict && strict, this, parts, type)); + return; + } + + var len = parts.length; + for (; i < len; i++) { + /* eslint-disable no-loop-func */ + this.replaceStack(function (current) { + var lookup = _this2.nameLookup(current, parts[i], type); + // We want to ensure that zero and false are handled properly if the context (falsy flag) + // needs to have the special handling for these values. + if (!falsy) { + return [' != null ? ', lookup, ' : ', current]; + } else { + // Otherwise we can use generic falsy handling + return [' && ', lookup]; + } + }); + /* eslint-enable no-loop-func */ + } + }, + + // [resolvePossibleLambda] + // + // On stack, before: value, ... + // On stack, after: resolved value, ... + // + // If the `value` is a lambda, replace it on the stack by + // the return value of the lambda + resolvePossibleLambda: function resolvePossibleLambda() { + this.push([this.aliasable('container.lambda'), '(', this.popStack(), ', ', this.contextName(0), ')']); + }, + + // [pushStringParam] + // + // On stack, before: ... + // On stack, after: string, currentContext, ... + // + // This opcode is designed for use in string mode, which + // provides the string value of a parameter along with its + // depth rather than resolving it immediately. + pushStringParam: function pushStringParam(string, type) { + this.pushContext(); + this.pushString(type); + + // If it's a subexpression, the string result + // will be pushed after this opcode. + if (type !== 'SubExpression') { + if (typeof string === 'string') { + this.pushString(string); + } else { + this.pushStackLiteral(string); + } + } + }, + + emptyHash: function emptyHash(omitEmpty) { + if (this.trackIds) { + this.push('{}'); // hashIds + } + if (this.stringParams) { + this.push('{}'); // hashContexts + this.push('{}'); // hashTypes + } + this.pushStackLiteral(omitEmpty ? 'undefined' : '{}'); + }, + pushHash: function pushHash() { + if (this.hash) { + this.hashes.push(this.hash); + } + this.hash = { values: {}, types: [], contexts: [], ids: [] }; + }, + popHash: function popHash() { + var hash = this.hash; + this.hash = this.hashes.pop(); + + if (this.trackIds) { + this.push(this.objectLiteral(hash.ids)); + } + if (this.stringParams) { + this.push(this.objectLiteral(hash.contexts)); + this.push(this.objectLiteral(hash.types)); + } + + this.push(this.objectLiteral(hash.values)); + }, + + // [pushString] + // + // On stack, before: ... + // On stack, after: quotedString(string), ... + // + // Push a quoted version of `string` onto the stack + pushString: function pushString(string) { + this.pushStackLiteral(this.quotedString(string)); + }, + + // [pushLiteral] + // + // On stack, before: ... + // On stack, after: value, ... + // + // Pushes a value onto the stack. This operation prevents + // the compiler from creating a temporary variable to hold + // it. + pushLiteral: function pushLiteral(value) { + this.pushStackLiteral(value); + }, + + // [pushProgram] + // + // On stack, before: ... + // On stack, after: program(guid), ... + // + // Push a program expression onto the stack. This takes + // a compile-time guid and converts it into a runtime-accessible + // expression. + pushProgram: function pushProgram(guid) { + if (guid != null) { + this.pushStackLiteral(this.programExpression(guid)); + } else { + this.pushStackLiteral(null); + } + }, + + // [registerDecorator] + // + // On stack, before: hash, program, params..., ... + // On stack, after: ... + // + // Pops off the decorator's parameters, invokes the decorator, + // and inserts the decorator into the decorators list. + registerDecorator: function registerDecorator(paramSize, name) { + var foundDecorator = this.nameLookup('decorators', name, 'decorator'), + options = this.setupHelperArgs(name, paramSize); + + this.decorators.push(['fn = ', this.decorators.functionCall(foundDecorator, '', ['fn', 'props', 'container', options]), ' || fn;']); + }, + + // [invokeHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // Pops off the helper's parameters, invokes the helper, + // and pushes the helper's return value onto the stack. + // + // If the helper is not found, `helperMissing` is called. + invokeHelper: function invokeHelper(paramSize, name, isSimple) { + var nonHelper = this.popStack(), + helper = this.setupHelper(paramSize, name); + + var possibleFunctionCalls = []; + + if (isSimple) { + // direct call to helper + possibleFunctionCalls.push(helper.name); + } + // call a function from the input object + possibleFunctionCalls.push(nonHelper); + if (!this.options.strict) { + possibleFunctionCalls.push(this.aliasable('container.hooks.helperMissing')); + } + + var functionLookupCode = ['(', this.itemsSeparatedBy(possibleFunctionCalls, '||'), ')']; + var functionCall = this.source.functionCall(functionLookupCode, 'call', helper.callParams); + this.push(functionCall); + }, + + itemsSeparatedBy: function itemsSeparatedBy(items, separator) { + var result = []; + result.push(items[0]); + for (var i = 1; i < items.length; i++) { + result.push(separator, items[i]); + } + return result; + }, + // [invokeKnownHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // This operation is used when the helper is known to exist, + // so a `helperMissing` fallback is not required. + invokeKnownHelper: function invokeKnownHelper(paramSize, name) { + var helper = this.setupHelper(paramSize, name); + this.push(this.source.functionCall(helper.name, 'call', helper.callParams)); + }, + + // [invokeAmbiguous] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of disambiguation + // + // This operation is used when an expression like `{{foo}}` + // is provided, but we don't know at compile-time whether it + // is a helper or a path. + // + // This operation emits more code than the other options, + // and can be avoided by passing the `knownHelpers` and + // `knownHelpersOnly` flags at compile-time. + invokeAmbiguous: function invokeAmbiguous(name, helperCall) { + this.useRegister('helper'); + + var nonHelper = this.popStack(); + + this.emptyHash(); + var helper = this.setupHelper(0, name, helperCall); + + var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper'); + + var lookup = ['(', '(helper = ', helperName, ' || ', nonHelper, ')']; + if (!this.options.strict) { + lookup[0] = '(helper = '; + lookup.push(' != null ? helper : ', this.aliasable('container.hooks.helperMissing')); + } + + this.push(['(', lookup, helper.paramsInit ? ['),(', helper.paramsInit] : [], '),', '(typeof helper === ', this.aliasable('"function"'), ' ? ', this.source.functionCall('helper', 'call', helper.callParams), ' : helper))']); + }, + + // [invokePartial] + // + // On stack, before: context, ... + // On stack after: result of partial invocation + // + // This operation pops off a context, invokes a partial with that context, + // and pushes the result of the invocation back. + invokePartial: function invokePartial(isDynamic, name, indent) { + var params = [], + options = this.setupParams(name, 1, params); + + if (isDynamic) { + name = this.popStack(); + delete options.name; + } + + if (indent) { + options.indent = JSON.stringify(indent); + } + options.helpers = 'helpers'; + options.partials = 'partials'; + options.decorators = 'container.decorators'; + + if (!isDynamic) { + params.unshift(this.nameLookup('partials', name, 'partial')); + } else { + params.unshift(name); + } + + if (this.options.compat) { + options.depths = 'depths'; + } + options = this.objectLiteral(options); + params.push(options); + + this.push(this.source.functionCall('container.invokePartial', '', params)); + }, + + // [assignToHash] + // + // On stack, before: value, ..., hash, ... + // On stack, after: ..., hash, ... + // + // Pops a value off the stack and assigns it to the current hash + assignToHash: function assignToHash(key) { + var value = this.popStack(), + context = undefined, + type = undefined, + id = undefined; + + if (this.trackIds) { + id = this.popStack(); + } + if (this.stringParams) { + type = this.popStack(); + context = this.popStack(); + } + + var hash = this.hash; + if (context) { + hash.contexts[key] = context; + } + if (type) { + hash.types[key] = type; + } + if (id) { + hash.ids[key] = id; + } + hash.values[key] = value; + }, + + pushId: function pushId(type, name, child) { + if (type === 'BlockParam') { + this.pushStackLiteral('blockParams[' + name[0] + '].path[' + name[1] + ']' + (child ? ' + ' + JSON.stringify('.' + child) : '')); + } else if (type === 'PathExpression') { + this.pushString(name); + } else if (type === 'SubExpression') { + this.pushStackLiteral('true'); + } else { + this.pushStackLiteral('null'); + } + }, + + // HELPERS + + compiler: JavaScriptCompiler, + + compileChildren: function compileChildren(environment, options) { + var children = environment.children, + child = undefined, + compiler = undefined; + + for (var i = 0, l = children.length; i < l; i++) { + child = children[i]; + compiler = new this.compiler(); // eslint-disable-line new-cap + + var existing = this.matchExistingProgram(child); + + if (existing == null) { + this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children + var index = this.context.programs.length; + child.index = index; + child.name = 'program' + index; + this.context.programs[index] = compiler.compile(child, options, this.context, !this.precompile); + this.context.decorators[index] = compiler.decorators; + this.context.environments[index] = child; + + this.useDepths = this.useDepths || compiler.useDepths; + this.useBlockParams = this.useBlockParams || compiler.useBlockParams; + child.useDepths = this.useDepths; + child.useBlockParams = this.useBlockParams; + } else { + child.index = existing.index; + child.name = 'program' + existing.index; + + this.useDepths = this.useDepths || existing.useDepths; + this.useBlockParams = this.useBlockParams || existing.useBlockParams; + } + } + }, + matchExistingProgram: function matchExistingProgram(child) { + for (var i = 0, len = this.context.environments.length; i < len; i++) { + var environment = this.context.environments[i]; + if (environment && environment.equals(child)) { + return environment; + } + } + }, + + programExpression: function programExpression(guid) { + var child = this.environment.children[guid], + programParams = [child.index, 'data', child.blockParams]; + + if (this.useBlockParams || this.useDepths) { + programParams.push('blockParams'); + } + if (this.useDepths) { + programParams.push('depths'); + } + + return 'container.program(' + programParams.join(', ') + ')'; + }, + + useRegister: function useRegister(name) { + if (!this.registers[name]) { + this.registers[name] = true; + this.registers.list.push(name); + } + }, + + push: function push(expr) { + if (!(expr instanceof Literal)) { + expr = this.source.wrap(expr); + } + + this.inlineStack.push(expr); + return expr; + }, + + pushStackLiteral: function pushStackLiteral(item) { + this.push(new Literal(item)); + }, + + pushSource: function pushSource(source) { + if (this.pendingContent) { + this.source.push(this.appendToBuffer(this.source.quotedString(this.pendingContent), this.pendingLocation)); + this.pendingContent = undefined; + } + + if (source) { + this.source.push(source); + } + }, + + replaceStack: function replaceStack(callback) { + var prefix = ['('], + stack = undefined, + createdStack = undefined, + usedLiteral = undefined; + + /* istanbul ignore next */ + if (!this.isInline()) { + throw new _exception2['default']('replaceStack on non-inline'); + } + + // We want to merge the inline statement into the replacement statement via ',' + var top = this.popStack(true); + + if (top instanceof Literal) { + // Literals do not need to be inlined + stack = [top.value]; + prefix = ['(', stack]; + usedLiteral = true; + } else { + // Get or create the current stack name for use by the inline + createdStack = true; + var _name = this.incrStack(); + + prefix = ['((', this.push(_name), ' = ', top, ')']; + stack = this.topStack(); + } + + var item = callback.call(this, stack); + + if (!usedLiteral) { + this.popStack(); + } + if (createdStack) { + this.stackSlot--; + } + this.push(prefix.concat(item, ')')); + }, + + incrStack: function incrStack() { + this.stackSlot++; + if (this.stackSlot > this.stackVars.length) { + this.stackVars.push('stack' + this.stackSlot); + } + return this.topStackName(); + }, + topStackName: function topStackName() { + return 'stack' + this.stackSlot; + }, + flushInline: function flushInline() { + var inlineStack = this.inlineStack; + this.inlineStack = []; + for (var i = 0, len = inlineStack.length; i < len; i++) { + var entry = inlineStack[i]; + /* istanbul ignore if */ + if (entry instanceof Literal) { + this.compileStack.push(entry); + } else { + var stack = this.incrStack(); + this.pushSource([stack, ' = ', entry, ';']); + this.compileStack.push(stack); + } + } + }, + isInline: function isInline() { + return this.inlineStack.length; + }, + + popStack: function popStack(wrapped) { + var inline = this.isInline(), + item = (inline ? this.inlineStack : this.compileStack).pop(); + + if (!wrapped && item instanceof Literal) { + return item.value; + } else { + if (!inline) { + /* istanbul ignore next */ + if (!this.stackSlot) { + throw new _exception2['default']('Invalid stack pop'); + } + this.stackSlot--; + } + return item; + } + }, + + topStack: function topStack() { + var stack = this.isInline() ? this.inlineStack : this.compileStack, + item = stack[stack.length - 1]; + + /* istanbul ignore if */ + if (item instanceof Literal) { + return item.value; + } else { + return item; + } + }, + + contextName: function contextName(context) { + if (this.useDepths && context) { + return 'depths[' + context + ']'; + } else { + return 'depth' + context; + } + }, + + quotedString: function quotedString(str) { + return this.source.quotedString(str); + }, + + objectLiteral: function objectLiteral(obj) { + return this.source.objectLiteral(obj); + }, + + aliasable: function aliasable(name) { + var ret = this.aliases[name]; + if (ret) { + ret.referenceCount++; + return ret; + } + + ret = this.aliases[name] = this.source.wrap(name); + ret.aliasable = true; + ret.referenceCount = 1; + + return ret; + }, + + setupHelper: function setupHelper(paramSize, name, blockHelper) { + var params = [], + paramsInit = this.setupHelperArgs(name, paramSize, params, blockHelper); + var foundHelper = this.nameLookup('helpers', name, 'helper'), + callContext = this.aliasable(this.contextName(0) + ' != null ? ' + this.contextName(0) + ' : (container.nullContext || {})'); + + return { + params: params, + paramsInit: paramsInit, + name: foundHelper, + callParams: [callContext].concat(params) + }; + }, + + setupParams: function setupParams(helper, paramSize, params) { + var options = {}, + contexts = [], + types = [], + ids = [], + objectArgs = !params, + param = undefined; + + if (objectArgs) { + params = []; + } + + options.name = this.quotedString(helper); + options.hash = this.popStack(); + + if (this.trackIds) { + options.hashIds = this.popStack(); + } + if (this.stringParams) { + options.hashTypes = this.popStack(); + options.hashContexts = this.popStack(); + } + + var inverse = this.popStack(), + program = this.popStack(); + + // Avoid setting fn and inverse if neither are set. This allows + // helpers to do a check for `if (options.fn)` + if (program || inverse) { + options.fn = program || 'container.noop'; + options.inverse = inverse || 'container.noop'; + } + + // The parameters go on to the stack in order (making sure that they are evaluated in order) + // so we need to pop them off the stack in reverse order + var i = paramSize; + while (i--) { + param = this.popStack(); + params[i] = param; + + if (this.trackIds) { + ids[i] = this.popStack(); + } + if (this.stringParams) { + types[i] = this.popStack(); + contexts[i] = this.popStack(); + } + } + + if (objectArgs) { + options.args = this.source.generateArray(params); + } + + if (this.trackIds) { + options.ids = this.source.generateArray(ids); + } + if (this.stringParams) { + options.types = this.source.generateArray(types); + options.contexts = this.source.generateArray(contexts); + } + + if (this.options.data) { + options.data = 'data'; + } + if (this.useBlockParams) { + options.blockParams = 'blockParams'; + } + return options; + }, + + setupHelperArgs: function setupHelperArgs(helper, paramSize, params, useRegister) { + var options = this.setupParams(helper, paramSize, params); + options.loc = JSON.stringify(this.source.currentLocation); + options = this.objectLiteral(options); + if (useRegister) { + this.useRegister('options'); + params.push('options'); + return ['options=', options]; + } else if (params) { + params.push(options); + return ''; + } else { + return options; + } + } + }; + + (function () { + var reservedWords = ('break else new var' + ' case finally return void' + ' catch for switch while' + ' continue function this with' + ' default if throw' + ' delete in try' + ' do instanceof typeof' + ' abstract enum int short' + ' boolean export interface static' + ' byte extends long super' + ' char final native synchronized' + ' class float package throws' + ' const goto private transient' + ' debugger implements protected volatile' + ' double import public let yield await' + ' null true false').split(' '); + + var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; + + for (var i = 0, l = reservedWords.length; i < l; i++) { + compilerWords[reservedWords[i]] = true; + } + })(); + + /** + * @deprecated May be removed in the next major version + */ + JavaScriptCompiler.isValidJavaScriptVariableName = function (name) { + return !JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name); + }; + + function strictLookup(requireTerminal, compiler, parts, type) { + var stack = compiler.popStack(), + i = 0, + len = parts.length; + if (requireTerminal) { + len--; + } + + for (; i < len; i++) { + stack = compiler.nameLookup(stack, parts[i], type); + } + + if (requireTerminal) { + return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[i]), ', ', JSON.stringify(compiler.source.currentLocation), ' )']; + } else { + return stack; + } + } + + exports['default'] = JavaScriptCompiler; + module.exports = exports['default']; + +/***/ }), +/* 53 */ +/***/ (function(module, exports, __webpack_require__) { + + /* global define */ + 'use strict'; + + var _Object$keys = __webpack_require__(13)['default']; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var SourceNode = undefined; + + try { + /* istanbul ignore next */ + if (false) { + // We don't support this in AMD environments. For these environments, we asusme that + // they are running on the browser and thus have no need for the source-map library. + var SourceMap = require('source-map'); + SourceNode = SourceMap.SourceNode; + } + } catch (err) {} + /* NOP */ + + /* istanbul ignore if: tested but not covered in istanbul due to dist build */ + if (!SourceNode) { + SourceNode = function (line, column, srcFile, chunks) { + this.src = ''; + if (chunks) { + this.add(chunks); + } + }; + /* istanbul ignore next */ + SourceNode.prototype = { + add: function add(chunks) { + if (_utils.isArray(chunks)) { + chunks = chunks.join(''); + } + this.src += chunks; + }, + prepend: function prepend(chunks) { + if (_utils.isArray(chunks)) { + chunks = chunks.join(''); + } + this.src = chunks + this.src; + }, + toStringWithSourceMap: function toStringWithSourceMap() { + return { code: this.toString() }; + }, + toString: function toString() { + return this.src; + } + }; + } + + function castChunk(chunk, codeGen, loc) { + if (_utils.isArray(chunk)) { + var ret = []; + + for (var i = 0, len = chunk.length; i < len; i++) { + ret.push(codeGen.wrap(chunk[i], loc)); + } + return ret; + } else if (typeof chunk === 'boolean' || typeof chunk === 'number') { + // Handle primitives that the SourceNode will throw up on + return chunk + ''; + } + return chunk; + } + + function CodeGen(srcFile) { + this.srcFile = srcFile; + this.source = []; + } + + CodeGen.prototype = { + isEmpty: function isEmpty() { + return !this.source.length; + }, + prepend: function prepend(source, loc) { + this.source.unshift(this.wrap(source, loc)); + }, + push: function push(source, loc) { + this.source.push(this.wrap(source, loc)); + }, + + merge: function merge() { + var source = this.empty(); + this.each(function (line) { + source.add([' ', line, '\n']); + }); + return source; + }, + + each: function each(iter) { + for (var i = 0, len = this.source.length; i < len; i++) { + iter(this.source[i]); + } + }, + + empty: function empty() { + var loc = this.currentLocation || { start: {} }; + return new SourceNode(loc.start.line, loc.start.column, this.srcFile); + }, + wrap: function wrap(chunk) { + var loc = arguments.length <= 1 || arguments[1] === undefined ? this.currentLocation || { start: {} } : arguments[1]; + + if (chunk instanceof SourceNode) { + return chunk; + } + + chunk = castChunk(chunk, this, loc); + + return new SourceNode(loc.start.line, loc.start.column, this.srcFile, chunk); + }, + + functionCall: function functionCall(fn, type, params) { + params = this.generateList(params); + return this.wrap([fn, type ? '.' + type + '(' : '(', params, ')']); + }, + + quotedString: function quotedString(str) { + return '"' + (str + '').replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 + .replace(/\u2029/g, '\\u2029') + '"'; + }, + + objectLiteral: function objectLiteral(obj) { + // istanbul ignore next + + var _this = this; + + var pairs = []; + + _Object$keys(obj).forEach(function (key) { + var value = castChunk(obj[key], _this); + if (value !== 'undefined') { + pairs.push([_this.quotedString(key), ':', value]); + } + }); + + var ret = this.generateList(pairs); + ret.prepend('{'); + ret.add('}'); + return ret; + }, + + generateList: function generateList(entries) { + var ret = this.empty(); + + for (var i = 0, len = entries.length; i < len; i++) { + if (i) { + ret.add(','); + } + + ret.add(castChunk(entries[i], this)); + } + + return ret; + }, + + generateArray: function generateArray(entries) { + var ret = this.generateList(entries); + ret.prepend('['); + ret.add(']'); + + return ret; + } + }; + + exports['default'] = CodeGen; + module.exports = exports['default']; + +/***/ }) +/******/ ]) +}); +; \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/lib/helpers.js b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/lib/helpers.js new file mode 100644 index 00000000..fe3964a0 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Files/template-rendering/lib/helpers.js @@ -0,0 +1,44 @@ +/*global Handlebars */ +var renderTemplate = (function (handlebars, undefined) { + 'use strict'; + + var compilationOptions = { + "knownHelpers": { + "link": true + }, + "knownHelpersOnly": true + }; + + handlebars.registerHelper('link', function (text, url, newWindow) { + var escapedText = handlebars.Utils.escapeExpression(text), + escapedUrl = handlebars.Utils.escapeExpression(url) + ; + + return new handlebars.SafeString( + '' + escapedText + '' + ); + }); + + /** + * Renders a Handlebars templates + * + * @param {String} sourceCode - Source code of Handlebars template + * @param {String} serializedData - A string containing JSON data + * @returns {String} HTML code + * @expose + */ + function renderTemplate(sourceCode, serializedData) { + var data, + template, + content + ; + + template = handlebars.compile(sourceCode, compilationOptions); + data = JSON.parse(serializedData); + content = template(data); + + return content; + } + + return renderTemplate; +}(Handlebars)); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/HostObjectsEmbeddingBenchmark.cs b/test/JavaScriptEngineSwitcher.Benchmarks/HostObjectsEmbeddingBenchmark.cs new file mode 100644 index 00000000..bac0c053 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/HostObjectsEmbeddingBenchmark.cs @@ -0,0 +1,181 @@ +using System; +using System.Text; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Order; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.V8; + +using JavaScriptEngineSwitcher.Benchmarks.Interop.ObjectsEmbedding; + +namespace JavaScriptEngineSwitcher.Benchmarks +{ + [MemoryDiagnoser] + [Orderer(SummaryOrderPolicy.Method, MethodOrderPolicy.Declared)] + public class HostObjectsEmbeddingBenchmark + { + private static void EmbedAndUseHostObjects(Func createJsEngine) + { + // Arrange + var someObj = new SomeClass(); + var logBuilder = new StringBuilder(); + Action log = (string value) => + { + logBuilder.AppendLine(value); + }; + + const string input = @"(function(someObj, log, undefined) { + var arg1, arg2, arg3, arg4, interimResult, result; + + log('-= Start code execution =-'); + + someObj.Field1 = false; + someObj.Field2 = 678; + someObj.Field3 = 2.20; + someObj.Field4 = 'QWERTY'; + someObj.Field5.X = 2; + someObj.Field5.Y = 4; + + someObj.Property1 = true; + someObj.Property2 = 711; + someObj.Property3 = 5.5; + someObj.Property4 = 'ЙЦУКЕН'; + someObj.Property5.Field1 = true; + someObj.Property5.Field2 = 611; + someObj.Property5.Field3 = 69.82; + someObj.Property5.Field4 = 'ASDF'; + someObj.Property5.Property1 = false; + someObj.Property5.Property2 = 555; + someObj.Property5.Property3 = 79.99; + someObj.Property5.Property4 = 'ФЫВА'; + + arg1 = someObj.Field1 || someObj.Property1; + arg2 = someObj.Field2 + someObj.Property2 + someObj.Field5.X; + arg3 = someObj.Field3 + someObj.Property3 + someObj.Field5.Y; + arg4 = someObj.Field4 + someObj.Property4; + + interimResult = someObj.DoSomething(arg1, arg2, arg3, arg4); + + arg1 = someObj.Property5.Field1 && someObj.Property5.Property1; + arg2 = interimResult - someObj.Property5.Field2 - someObj.Property5.Property2; + arg3 = someObj.Property5.Field3 / someObj.Property5.Property3; + arg4 = someObj.Property5.Field4 + someObj.Property5.Property4; + + result = someObj.Property5.DoSomething(arg1, arg2, arg3, arg4); + + log('-= End of code execution =-'); + + return result; +}(someObj, log));"; + const string targetOutput = "RmFsc2V8MjkxNHwwLjg3Mjg1OTEwNzM4ODQyNHxBU0RG0KTQq9CS0JA="; + string targetLogOutput = "-= Start code execution =-" + Environment.NewLine + + "-= End of code execution =-" + Environment.NewLine; + + // Act + string output; + string logOutput; + + using (var jsEngine = createJsEngine()) + { + jsEngine.EmbedHostObject("someObj", someObj); + jsEngine.EmbedHostObject("log", log); + + output = jsEngine.Evaluate(input); + + logOutput = logBuilder.ToString(); + logBuilder.Clear(); + } + + // Assert + Assert.Equal(targetOutput, output); + Assert.Equal(targetLogOutput, logOutput); + } + + [Benchmark] + public void ChakraCore() + { + Func createJsEngine = () => new ChakraCoreJsEngine(); + EmbedAndUseHostObjects(createJsEngine); + } + + [Benchmark] + public void Jint() + { + Func createJsEngine = () => new JintJsEngine(); + EmbedAndUseHostObjects(createJsEngine); + } + + [Benchmark] + public void Jurassic() + { + Func createJsEngine = () => new JurassicJsEngine(); + EmbedAndUseHostObjects(createJsEngine); + } +#if NET462 + + [Benchmark] + public void MsieClassic() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.Classic + }); + EmbedAndUseHostObjects(createJsEngine); + } + + [Benchmark] + public void MsieChakraActiveScript() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraActiveScript + }); + EmbedAndUseHostObjects(createJsEngine); + } +#endif + [Benchmark] + public void MsieChakraIeJsRt() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraIeJsRt + }); + EmbedAndUseHostObjects(createJsEngine); + } + + [Benchmark] + public void MsieChakraEdgeJsRt() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraEdgeJsRt + }); + EmbedAndUseHostObjects(createJsEngine); + } + + [Benchmark] + public void NiL() + { + Func createJsEngine = () => new NiLJsEngine(); + EmbedAndUseHostObjects(createJsEngine); + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void V8(bool disableDynamicBinding) + { + Func createJsEngine = () => new V8JsEngine( + new V8Settings { DisableDynamicBinding = disableDynamicBinding } + ); + EmbedAndUseHostObjects(createJsEngine); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/HostTypesEmbeddingBenchmark.cs b/test/JavaScriptEngineSwitcher.Benchmarks/HostTypesEmbeddingBenchmark.cs new file mode 100644 index 00000000..1793ae43 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/HostTypesEmbeddingBenchmark.cs @@ -0,0 +1,161 @@ +using System; +using System.Drawing; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Order; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.V8; + +using JavaScriptEngineSwitcher.Benchmarks.Interop.TypesEmbedding; + +namespace JavaScriptEngineSwitcher.Benchmarks +{ + [MemoryDiagnoser] + [Orderer(SummaryOrderPolicy.Method, MethodOrderPolicy.Declared)] + public class HostTypesEmbeddingBenchmark + { + private static void EmbedAndUseHostTypes(Func createJsEngine) + { + // Arrange + var someType = typeof(SomeClass); + var pointType = typeof(Point); + var someOtherType = typeof(SomeOtherClass); + + const string input = @"(function(SomeClass, Point, SomeOtherClass, undefined) { + var arg1, arg2, arg3, arg4, interimResult, result; + + SomeClass.Field1 = false; + SomeClass.Field2 = 678; + SomeClass.Field3 = 2.20; + SomeClass.Field4 = 'QWERTY'; + SomeClass.Field5 = new Point(2, 4); + + SomeClass.Property1 = true; + SomeClass.Property2 = 711; + SomeClass.Property3 = 5.5; + SomeClass.Property4 = 'ЙЦУКЕН'; + SomeClass.Property5 = new SomeOtherClass(true, 611, 69.82, 'ASDF', + false, 555, 79.99, 'ФЫВА'); + + arg1 = SomeClass.Field1 || SomeClass.Property1; + arg2 = SomeClass.Field2 + SomeClass.Property2 + SomeClass.Field5.X; + arg3 = SomeClass.Field3 + SomeClass.Property3 + SomeClass.Field5.Y; + arg4 = SomeClass.Field4 + SomeClass.Property4; + + interimResult = SomeClass.DoSomething(arg1, arg2, arg3, arg4); + + arg1 = SomeClass.Property5.Field1 && SomeClass.Property5.Property1; + arg2 = interimResult - SomeClass.Property5.Field2 - SomeClass.Property5.Property2; + arg3 = SomeClass.Property5.Field3 / SomeClass.Property5.Property3; + arg4 = SomeClass.Property5.Field4 + SomeClass.Property5.Property4; + + result = SomeOtherClass.DoSomething(arg1, arg2, arg3, arg4); + + return result; +}(SomeClass, Point, SomeOtherClass));"; + const string targetOutput = "RmFsc2V8MjkyMHwwLjg3Mjg1OTEwNzM4ODQyNHxBU0RG0KTQq9CS0JA="; + + // Act + string output; + + using (var jsEngine = createJsEngine()) + { + jsEngine.EmbedHostType("SomeClass", someType); + jsEngine.EmbedHostType("Point", pointType); + jsEngine.EmbedHostType("SomeOtherClass", someOtherType); + + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Benchmark] + public void ChakraCore() + { + Func createJsEngine = () => new ChakraCoreJsEngine(); + EmbedAndUseHostTypes(createJsEngine); + } + + [Benchmark] + public void Jint() + { + Func createJsEngine = () => new JintJsEngine(); + EmbedAndUseHostTypes(createJsEngine); + } + + [Benchmark] + public void Jurassic() + { + Func createJsEngine = () => new JurassicJsEngine(); + EmbedAndUseHostTypes(createJsEngine); + } +#if NET462 + + [Benchmark] + public void MsieClassic() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.Classic + }); + EmbedAndUseHostTypes(createJsEngine); + } + + [Benchmark] + public void MsieChakraActiveScript() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraActiveScript + }); + EmbedAndUseHostTypes(createJsEngine); + } +#endif + [Benchmark] + public void MsieChakraIeJsRt() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraIeJsRt + }); + EmbedAndUseHostTypes(createJsEngine); + } + + [Benchmark] + public void MsieChakraEdgeJsRt() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraEdgeJsRt + }); + EmbedAndUseHostTypes(createJsEngine); + } + + [Benchmark] + public void NiL() + { + Func createJsEngine = () => new NiLJsEngine(); + EmbedAndUseHostTypes(createJsEngine); + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void V8(bool disableDynamicBinding) + { + Func createJsEngine = () => new V8JsEngine( + new V8Settings { DisableDynamicBinding = disableDynamicBinding } + ); + EmbedAndUseHostTypes(createJsEngine); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Interop/ObjectsEmbedding/SomeClass.cs b/test/JavaScriptEngineSwitcher.Benchmarks/Interop/ObjectsEmbedding/SomeClass.cs new file mode 100644 index 00000000..a27f82f0 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Interop/ObjectsEmbedding/SomeClass.cs @@ -0,0 +1,34 @@ +using System; +using System.Drawing; +using System.Linq; +using System.Text; + +namespace JavaScriptEngineSwitcher.Benchmarks.Interop.ObjectsEmbedding +{ + public class SomeClass : SomeClassBase + { + public Point Field5; + + public SomeOtherClass Property5 { get; set; } + + + public SomeClass() + { + Field5 = new Point(); + + Property5 = new SomeOtherClass(); + } + + + public int DoSomething(bool arg1, int arg2, double arg3, string arg4) + { + int result = Convert.ToInt32(arg1) + + arg2 + + (int)Math.Ceiling(arg3) + + Encoding.UTF8.GetBytes(arg4).Sum(x => x); + ; + + return result; + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Interop/ObjectsEmbedding/SomeClassBase.cs b/test/JavaScriptEngineSwitcher.Benchmarks/Interop/ObjectsEmbedding/SomeClassBase.cs new file mode 100644 index 00000000..fa8ceb59 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Interop/ObjectsEmbedding/SomeClassBase.cs @@ -0,0 +1,15 @@ +namespace JavaScriptEngineSwitcher.Benchmarks.Interop.ObjectsEmbedding +{ + public abstract class SomeClassBase + { + public bool Field1; + public int Field2; + public double Field3; + public string Field4; + + public bool Property1 { get; set; } + public int Property2 { get; set; } + public double Property3 { get; set; } + public string Property4 { get; set; } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Interop/ObjectsEmbedding/SomeOtherClass.cs b/test/JavaScriptEngineSwitcher.Benchmarks/Interop/ObjectsEmbedding/SomeOtherClass.cs new file mode 100644 index 00000000..2dc857a9 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Interop/ObjectsEmbedding/SomeOtherClass.cs @@ -0,0 +1,21 @@ +using System; +using System.Globalization; +using System.Text; + +namespace JavaScriptEngineSwitcher.Benchmarks.Interop.ObjectsEmbedding +{ + public class SomeOtherClass : SomeClassBase + { + public string DoSomething(bool arg1, int arg2, double arg3, string arg4) + { + string rawResult = arg1.ToString(CultureInfo.InvariantCulture) + "|" + + arg2.ToString(CultureInfo.InvariantCulture) + "|" + + Math.Round(arg3, 15).ToString(CultureInfo.InvariantCulture) + "|" + + arg4 + ; + string result = Convert.ToBase64String(Encoding.UTF8.GetBytes(rawResult)); + + return result; + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Interop/TypesEmbedding/SomeClass.cs b/test/JavaScriptEngineSwitcher.Benchmarks/Interop/TypesEmbedding/SomeClass.cs new file mode 100644 index 00000000..240c26b0 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Interop/TypesEmbedding/SomeClass.cs @@ -0,0 +1,34 @@ +using System; +using System.Drawing; +using System.Linq; +using System.Text; + +namespace JavaScriptEngineSwitcher.Benchmarks.Interop.TypesEmbedding +{ + public static class SomeClass + { + public static bool Field1; + public static int Field2; + public static double Field3; + public static string Field4; + public static Point Field5; + + public static bool Property1 { get; set; } + public static int Property2 { get; set; } + public static double Property3 { get; set; } + public static string Property4 { get; set; } + public static SomeOtherClass Property5 { get; set; } + + + public static int DoSomething(bool arg1, int arg2, double arg3, string arg4) + { + int result = Convert.ToInt32(arg1) + + arg2 + + (int)Math.Ceiling(arg3) + + Encoding.UTF8.GetBytes(arg4).Sum(x => x); + ; + + return result; + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Interop/TypesEmbedding/SomeOtherClass.cs b/test/JavaScriptEngineSwitcher.Benchmarks/Interop/TypesEmbedding/SomeOtherClass.cs new file mode 100644 index 00000000..6c508de4 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Interop/TypesEmbedding/SomeOtherClass.cs @@ -0,0 +1,47 @@ +using System; +using System.Globalization; +using System.Text; + +namespace JavaScriptEngineSwitcher.Benchmarks.Interop.TypesEmbedding +{ + public class SomeOtherClass + { + public bool Field1; + public int Field2; + public double Field3; + public string Field4; + + public bool Property1 { get; set; } + public int Property2 { get; set; } + public double Property3 { get; set; } + public string Property4 { get; set; } + + + public SomeOtherClass(bool field1, int field2, double field3, string field4, + bool property1, int property2, double property3, string property4) + { + Field1 = field1; + Field2 = field2; + Field3 = field3; + Field4 = field4; + + Property1 = property1; + Property2 = property2; + Property3 = property3; + Property4 = property4; + } + + + public static string DoSomething(bool arg1, int arg2, double arg3, string arg4) + { + string rawResult = arg1.ToString(CultureInfo.InvariantCulture) + "|" + + arg2.ToString(CultureInfo.InvariantCulture) + "|" + + Math.Round(arg3, 15).ToString(CultureInfo.InvariantCulture) + "|" + + arg4 + ; + string result = Convert.ToBase64String(Encoding.UTF8.GetBytes(rawResult)); + + return result; + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/JavaScriptEngineSwitcher.Benchmarks.csproj b/test/JavaScriptEngineSwitcher.Benchmarks/JavaScriptEngineSwitcher.Benchmarks.csproj new file mode 100644 index 00000000..b78e72c9 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/JavaScriptEngineSwitcher.Benchmarks.csproj @@ -0,0 +1,66 @@ + + + + JS Engine Switcher: Benchmarks + 3.30.2 + net462;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0 + Exe + AnyCPU + true + true + true + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + Files/template-rendering/content/%(RecursiveDir)/%(Filename)%(Extension) + PreserveNewest + PreserveNewest + + + + \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/JsExecutionHeavyBenchmark.cs b/test/JavaScriptEngineSwitcher.Benchmarks/JsExecutionHeavyBenchmark.cs new file mode 100644 index 00000000..a3225779 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/JsExecutionHeavyBenchmark.cs @@ -0,0 +1,306 @@ +using System; +using System.IO; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Order; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.Node; +using JavaScriptEngineSwitcher.V8; +using JavaScriptEngineSwitcher.Vroom; +using JavaScriptEngineSwitcher.Yantra; + +namespace JavaScriptEngineSwitcher.Benchmarks +{ + [MemoryDiagnoser] + [Orderer(SummaryOrderPolicy.Method, MethodOrderPolicy.Declared)] + public class JsExecutionHeavyBenchmark + { + /// + /// Name of the file containing library for template rendering + /// + private const string LibraryFileName = "bundle.min.js"; + + /// + /// Name of template rendering function + /// + private const string FunctionName = "renderTemplate"; + + /// + /// Code of library for template rendering + /// + private static string _libraryCode; + + /// + /// List of items + /// + private static ContentItem[] _contentItems = new[] { + new ContentItem("hello-world"), + new ContentItem("contacts"), + new ContentItem("js-engines"), + new ContentItem("web-browser-family-tree") + }; + + + /// + /// Static constructor + /// + static JsExecutionHeavyBenchmark() + { + PopulateTestData(); + } + + + public static string GetAbsoluteDirectoryPath(string directoryPath) + { + string baseDirectoryPath = AppDomain.CurrentDomain.BaseDirectory; + string absoluteDirectoryPath = Path.GetFullPath(Path.Combine(baseDirectoryPath, directoryPath)); +#if NETCOREAPP + + if (!Directory.Exists(absoluteDirectoryPath)) + { + absoluteDirectoryPath = Path.GetFullPath( + Path.Combine(baseDirectoryPath, "../../../../", directoryPath)); + } +#endif + + return absoluteDirectoryPath; + } + + /// + /// Populates a test data + /// + public static void PopulateTestData() + { + string filesDirectoryPath = GetAbsoluteDirectoryPath("Files/template-rendering"); + string librariesDirectoryPath = Path.Combine(filesDirectoryPath, "lib"); + string contentDirectoryPath = Path.Combine(filesDirectoryPath, "content"); + + _libraryCode = File.ReadAllText(Path.Combine(librariesDirectoryPath, LibraryFileName)); + + foreach (ContentItem item in _contentItems) + { + string itemDirectoryPath = Path.Combine(contentDirectoryPath, item.Name); + + item.TemplateCode = File.ReadAllText(Path.Combine(itemDirectoryPath, "template.handlebars")); + item.SerializedData = File.ReadAllText(Path.Combine(itemDirectoryPath, "data.json")); + item.TargetOutput = File.ReadAllText(Path.Combine(itemDirectoryPath, "target-output.html")); + } + } + + /// + /// Render a templates + /// + /// Delegate for create an instance of the JS engine + /// Flag for whether to allow execution of JS code with pre-compilation + private static void RenderTemplates(Func createJsEngine, bool withPrecompilation) + { + // Arrange + IPrecompiledScript precompiledCode = null; + + // Act + using (var jsEngine = createJsEngine()) + { + if (withPrecompilation) + { + if (!jsEngine.SupportsScriptPrecompilation) + { + throw new NotSupportedException($"{jsEngine.Name} does not support precompilation."); + } + + precompiledCode = jsEngine.Precompile(_libraryCode, LibraryFileName); + jsEngine.Execute(precompiledCode); + } + else + { + jsEngine.Execute(_libraryCode, LibraryFileName); + } + + _contentItems[0].Output = jsEngine.CallFunction(FunctionName, _contentItems[0].TemplateCode, + _contentItems[0].SerializedData); + } + + for (int itemIndex = 1; itemIndex < _contentItems.Length; itemIndex++) + { + using (var jsEngine = createJsEngine()) + { + if (withPrecompilation) + { + jsEngine.Execute(precompiledCode); + } + else + { + jsEngine.Execute(_libraryCode, LibraryFileName); + } + _contentItems[itemIndex].Output = jsEngine.CallFunction(FunctionName, + _contentItems[itemIndex].TemplateCode, _contentItems[itemIndex].SerializedData); + } + } + + // Assert + foreach (ContentItem item in _contentItems) + { + Assert.Equal(item.TargetOutput, item.Output, true); + } + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void ChakraCore(bool withPrecompilation) + { + Func createJsEngine = () => new ChakraCoreJsEngine(); + RenderTemplates(createJsEngine, withPrecompilation); + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void Jint(bool withPrecompilation) + { + Func createJsEngine = () => new JintJsEngine(); + RenderTemplates(createJsEngine, withPrecompilation); + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void Jurassic(bool withPrecompilation) + { + Func createJsEngine = () => new JurassicJsEngine(); + RenderTemplates(createJsEngine, withPrecompilation); + } +#if NET462 + + [Benchmark] + public void MsieClassic() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.Classic, + UseJson2Library = true + }); + RenderTemplates(createJsEngine, false); + } + + [Benchmark] + public void MsieChakraActiveScript() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraActiveScript + }); + RenderTemplates(createJsEngine, false); + } +#endif + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void MsieChakraIeJsRt(bool withPrecompilation) + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraIeJsRt + }); + RenderTemplates(createJsEngine, withPrecompilation); + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void MsieChakraEdgeJsRt(bool withPrecompilation) + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraEdgeJsRt + }); + RenderTemplates(createJsEngine, withPrecompilation); + } + + [Benchmark] + public void NiL() + { + Func createJsEngine = () => new NiLJsEngine(); + RenderTemplates(createJsEngine, false); + } + + [Benchmark] + public void Node() + { + Func createJsEngine = () => new NodeJsEngine(); + RenderTemplates(createJsEngine, false); + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void V8(bool withPrecompilation) + { + Func createJsEngine = () => new V8JsEngine(); + RenderTemplates(createJsEngine, withPrecompilation); + } + + [Benchmark] + public void Vroom() + { + Func createJsEngine = () => new VroomJsEngine(); + RenderTemplates(createJsEngine, false); + } + + [Benchmark] + public void Yantra() + { + Func createJsEngine = () => new YantraJsEngine(); + RenderTemplates(createJsEngine, false); + } + + #region Internal types + + private sealed class ContentItem + { + public string Name + { + get; + set; + } + + public string TemplateCode + { + get; + set; + } + + public string SerializedData + { + get; + set; + } + + public string TargetOutput + { + get; + set; + } + + public string Output + { + get; + set; + } + + + public ContentItem(string name) + { + Name = name; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/JsExecutionLightBenchmark.cs b/test/JavaScriptEngineSwitcher.Benchmarks/JsExecutionLightBenchmark.cs new file mode 100644 index 00000000..d352072a --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/JsExecutionLightBenchmark.cs @@ -0,0 +1,297 @@ +using System; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Order; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Utilities; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.Node; +using JavaScriptEngineSwitcher.V8; +using JavaScriptEngineSwitcher.Vroom; +using JavaScriptEngineSwitcher.Yantra; + +namespace JavaScriptEngineSwitcher.Benchmarks +{ + [MemoryDiagnoser] + [Orderer(SummaryOrderPolicy.Method, MethodOrderPolicy.Declared)] + public class JsExecutionLightBenchmark + { + /// + /// Name of the file containing library for transliteration of Russian + /// + private const string LibraryFileName = "russian-translit.js"; + + /// + /// Name of transliterate function + /// + private const string FunctionName = "transliterate"; + + /// + /// Number of transliterated items + /// + private const int ItemCount = 7; + + /// + /// Code of library for transliteration of Russian + /// + private static string _libraryCode; + + /// + /// List of transliteration types + /// + private static string[] _inputTypes; + + /// + /// List of input strings + /// + private static string[] _inputStrings; + + /// + /// List of target output strings + /// + private static string[] _targetOutputStrings; + + + /// + /// Static constructor + /// + static JsExecutionLightBenchmark() + { + PopulateTestData(); + } + + + /// + /// Populates a test data + /// + public static void PopulateTestData() + { + _libraryCode = Utils.GetResourceAsString( + $"Resources.{LibraryFileName}", typeof(JsExecutionLightBenchmark)); + _inputTypes = new string[ItemCount] + { + "basic", "letters-numbers", "gost-16876-71", "gost-7-79-2000", "police", "foreign-passport", + "yandex-friendly-url" + }; + _inputStrings = new string[ItemCount] + { + "SOLID — мнемонический акроним, введённый Майклом Фэзерсом для первых пяти принципов, названных " + + "Робертом Мартином в начале 2000-х, которые означали пять основных принципов объектно-ориентированного " + + "программирования и проектирования.", + + "Принцип единственной ответственности (The Single Responsibility Principle). " + + "Каждый класс выполняет лишь одну задачу.", + + "Принцип открытости/закрытости (The Open Closed Principle). " + + "«программные сущности … должны быть открыты для расширения, но закрыты для модификации.»", + + "Принцип подстановки Барбары Лисков (The Liskov Substitution Principle). " + + "«объекты в программе должны быть заменяемыми на экземпляры их подтипов без изменения правильности выполнения программы.»", + + "Принцип разделения интерфейса (The Interface Segregation Principle). " + + "«много интерфейсов, специально предназначенных для клиентов, лучше, чем один интерфейс общего назначения.»", + + "Принцип инверсии зависимостей (The Dependency Inversion Principle). " + + "«Зависимость на Абстракциях. Нет зависимости на что-то конкретное.»", + + "SOLID (объектно-ориентированное программирование)" + }; + _targetOutputStrings = new string[ItemCount] + { + "SOLID — mnemonicheskij akronim, vvedjonnyj Majklom Fjezersom dlja pervyh pjati principov, nazvannyh " + + "Robertom Martinom v nachale 2000-h, kotorye oznachali pjat' osnovnyh principov ob#ektno-orientirovannogo " + + "programmirovanija i proektirovanija.", + + "Princip edinstvennoj otvetstvennosti (The Single Responsibility Principle). " + + "Ka#dyj klass vypolnjaet li6' odnu zada4u.", + + "Princip otkrytosti/zakrytosti (The Open Closed Principle). " + + "«programmnye sushhnosti … dolzhny byt' otkryty dlja rasshirenija, no zakryty dlja modifikacii.»", + + "Princip podstanovki Barbary Liskov (The Liskov Substitution Principle). " + + "«ob\"ekty v programme dolzhny byt' zamenyaemymi na e'kzemplyary ix podtipov bez izmeneniya pravil'nosti " + + "vypolneniya programmy.»", + + "Printsip razdeleniia interfeisa (The Interface Segregation Principle). " + + "«mnogo interfeisov, spetsialno prednaznachennykh dlia klientov, luchshe, chem odin interfeis obshchego " + + "naznacheniia.»", + + "Printcip inversii zavisimostei (The Dependency Inversion Principle). " + + "«Zavisimost na Abstraktciiakh. Net zavisimosti na chto-to konkretnoe.»", + + "solid-obektno-orientirovannoe-programmirovanie" + }; + } + + /// + /// Transliterates a strings + /// + /// Delegate for create an instance of the JS engine + /// Flag for whether to allow execution of JS code with pre-compilation + private static void TransliterateStrings(Func createJsEngine, bool withPrecompilation) + { + // Arrange + string[] outputStrings = new string[ItemCount]; + IPrecompiledScript precompiledCode = null; + + // Act + using (var jsEngine = createJsEngine()) + { + if (withPrecompilation) + { + if (!jsEngine.SupportsScriptPrecompilation) + { + throw new NotSupportedException($"{jsEngine.Name} does not support precompilation."); + } + + precompiledCode = jsEngine.Precompile(_libraryCode, LibraryFileName); + jsEngine.Execute(precompiledCode); + } + else + { + jsEngine.Execute(_libraryCode, LibraryFileName); + } + + outputStrings[0] = jsEngine.CallFunction(FunctionName, _inputStrings[0], _inputTypes[0]); + } + + for (int itemIndex = 1; itemIndex < ItemCount; itemIndex++) + { + using (var jsEngine = createJsEngine()) + { + if (withPrecompilation) + { + jsEngine.Execute(precompiledCode); + } + else + { + jsEngine.Execute(_libraryCode, LibraryFileName); + } + outputStrings[itemIndex] = jsEngine.CallFunction(FunctionName, _inputStrings[itemIndex], + _inputTypes[itemIndex]); + } + } + + // Assert + for (int itemIndex = 0; itemIndex < ItemCount; itemIndex++) + { + Assert.Equal(_targetOutputStrings[itemIndex], outputStrings[itemIndex]); + } + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void ChakraCore(bool withPrecompilation) + { + Func createJsEngine = () => new ChakraCoreJsEngine(); + TransliterateStrings(createJsEngine, withPrecompilation); + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void Jint(bool withPrecompilation) + { + Func createJsEngine = () => new JintJsEngine(); + TransliterateStrings(createJsEngine, withPrecompilation); + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void Jurassic(bool withPrecompilation) + { + Func createJsEngine = () => new JurassicJsEngine(); + TransliterateStrings(createJsEngine, withPrecompilation); + } +#if NET462 + + [Benchmark] + public void MsieClassic() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.Classic + }); + TransliterateStrings(createJsEngine, false); + } + + [Benchmark] + public void MsieChakraActiveScript() + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraActiveScript + }); + TransliterateStrings(createJsEngine, false); + } +#endif + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void MsieChakraIeJsRt(bool withPrecompilation) + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraIeJsRt + }); + TransliterateStrings(createJsEngine, withPrecompilation); + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void MsieChakraEdgeJsRt(bool withPrecompilation) + { + Func createJsEngine = () => new MsieJsEngine(new MsieSettings + { + EngineMode = JsEngineMode.ChakraEdgeJsRt + }); + TransliterateStrings(createJsEngine, withPrecompilation); + } + + [Benchmark] + public void NiL() + { + Func createJsEngine = () => new NiLJsEngine(); + TransliterateStrings(createJsEngine, false); + } + + [Benchmark] + public void Node() + { + Func createJsEngine = () => new NodeJsEngine(); + TransliterateStrings(createJsEngine, false); + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void V8(bool withPrecompilation) + { + Func createJsEngine = () => new V8JsEngine(); + TransliterateStrings(createJsEngine, withPrecompilation); + } + + [Benchmark] + public void Vroom() + { + Func createJsEngine = () => new VroomJsEngine(); + TransliterateStrings(createJsEngine, false); + } + + [Benchmark] + public void Yantra() + { + Func createJsEngine = () => new YantraJsEngine(); + TransliterateStrings(createJsEngine, false); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Program.cs b/test/JavaScriptEngineSwitcher.Benchmarks/Program.cs new file mode 100644 index 00000000..7745beac --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Program.cs @@ -0,0 +1,14 @@ +using System.Reflection; + +using BenchmarkDotNet.Running; + +namespace JavaScriptEngineSwitcher.Benchmarks +{ + public static class Program + { + public static void Main(string[] args) + { + BenchmarkSwitcher.FromAssembly(typeof(Program).GetTypeInfo().Assembly).Run(args); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/Resources/russian-translit.js b/test/JavaScriptEngineSwitcher.Benchmarks/Resources/russian-translit.js new file mode 100644 index 00000000..7e688663 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/Resources/russian-translit.js @@ -0,0 +1,942 @@ +var transliterate = (function () { + 'use strict'; + + /** + * Сопоставления русских и латинских символов, сгруппированные по типам (системам) транслитерации + * + * @private {Object} + */ + var characterMappings = { + // Основной + 'basic': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'jo', + 'ж': 'zh', + 'з': 'z', + 'и': 'i', + 'й': 'j', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'h', + 'ц': 'c', + 'ч': 'ch', + 'ш': 'sh', + 'щ': 'shh', + 'ъ': '#', + 'ы': 'y', + 'ь': '\'', + 'э': 'je', + 'ю': 'ju', + 'я': 'ja', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'Jo', + 'Ж': 'Zh', + 'З': 'Z', + 'И': 'I', + 'Й': 'J', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'H', + 'Ц': 'C', + 'Ч': 'Ch', + 'Ш': 'Sh', + 'Щ': 'Shh', + 'Ъ': '##', + 'Ы': 'Y', + 'Ь': '\'\'', + 'Э': 'Je', + 'Ю': 'Ju', + 'Я': 'Ja' + }, + + // Буквы-цифры + 'letters-numbers': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'jo', + 'ж': '#', + 'з': 'z', + 'и': 'i', + 'й': 'j', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'h', + 'ц': 'c', + 'ч': '4', + 'ш': '6', + 'щ': 'sch', + 'ъ': '*', + 'ы': 'y', + 'ь': '\'', + 'э': 'je', + 'ю': 'ju', + 'я': 'ja', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'Jo', + 'Ж': '##', + 'З': 'Z', + 'И': 'I', + 'Й': 'J', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'H', + 'Ц': 'C', + 'Ч': '44', + 'Ш': '66', + 'Щ': 'Sch', + 'Ъ': '**', + 'Ы': 'Y', + 'Ь': '\'\'', + 'Э': 'Je', + 'Ю': 'Ju', + 'Я': 'Ja' + }, + + // ГОСТ 16876-71 + 'gost-16876-71': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'jo', + 'ж': 'zh', + 'з': 'z', + 'и': 'i', + 'й': 'jj', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'kh', + 'ц': 'c', + 'ч': 'ch', + 'ш': 'sh', + 'щ': 'shh', + 'ъ': '"', + 'ы': 'y', + 'ь': '\'', + 'э': 'eh', + 'ю': 'ju', + 'я': 'ja', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'Jo', + 'Ж': 'Zh', + 'З': 'Z', + 'И': 'I', + 'Й': 'Jj', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'Kh', + 'Ц': 'C', + 'Ч': 'Ch', + 'Ш': 'Sh', + 'Щ': 'Shh', + 'Ъ': '""', + 'Ы': 'Y', + 'Ь': '\'\'', + 'Э': 'Eh', + 'Ю': 'Ju', + 'Я': 'Ja' + }, + + // ГОСТ 7.79-2000 + 'gost-7-79-2000': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'yo', + 'ж': 'zh', + 'з': 'z', + 'и': 'i', + 'й': 'j', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'x', + 'ц': 'c', + 'ч': 'ch', + 'ш': 'sh', + 'щ': 'shh', + 'ъ': '"', + 'ы': 'y', + 'ь': '\'', + 'э': 'e\'', + 'ю': 'yu', + 'я': 'ya', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'Yo', + 'Ж': 'Zh', + 'З': 'Z', + 'И': 'I', + 'Й': 'J', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'X', + 'Ц': 'C', + 'Ч': 'Ch', + 'Ш': 'Sh', + 'Щ': 'Shh', + 'Ъ': '""', + 'Ы': 'Y', + 'Ь': '\'\'', + 'Э': 'E\'', + 'Ю': 'Yu', + 'Я': 'Ya' + }, + + // СЭВ 1362-78 + 'sev-1362-78': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'jo', + 'ж': 'zh', + 'з': 'z', + 'и': 'i', + 'й': 'j', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'kh', + 'ц': 'c', + 'ч': 'ch', + 'ш': 'sh', + 'щ': 'shh', + 'ъ': '"', + 'ы': 'y', + 'ь': '\'', + 'э': 'eh', + 'ю': 'ju', + 'я': 'ja', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'Jo', + 'Ж': 'Zh', + 'З': 'Z', + 'И': 'I', + 'Й': 'J', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'Kh', + 'Ц': 'C', + 'Ч': 'Ch', + 'Ш': 'Sh', + 'Щ': 'Shh', + 'Ъ': '""', + 'Ы': 'Y', + 'Ь': '\'\'', + 'Э': 'Eh', + 'Ю': 'Ju', + 'Я': 'Ja' + }, + + // ISO 9:1995 + 'iso-9-1995': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'ë', + 'ж': 'ž', + 'з': 'z', + 'и': 'i', + 'й': 'j', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'h', + 'ц': 'c', + 'ч': 'č', + 'ш': 'š', + 'щ': 'ŝ', + 'ъ': '"', + 'ы': 'y', + 'ь': '\'', + 'э': 'è', + 'ю': 'û', + 'я': 'â', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'Ë', + 'Ж': 'Ž', + 'З': 'Z', + 'И': 'I', + 'Й': 'J', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'H', + 'Ц': 'C', + 'Ч': 'Č', + 'Ш': 'Š', + 'Щ': 'Ŝ', + 'Ъ': '"', + 'Ы': 'Y', + 'Ь': '\'', + 'Э': 'È', + 'Ю': 'Û', + 'Я': 'Â' + }, + + // LC + 'lc': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'e', + 'ж': 'zh', + 'з': 'z', + 'и': 'i', + 'й': 'i', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'kh', + 'ц': 'ts', + 'ч': 'ch', + 'ш': 'sh', + 'щ': 'shch', + 'ъ': '"', + 'ы': 'y', + 'ь': '\'', + 'э': 'e', + 'ю': 'iu', + 'я': 'ia', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'E', + 'Ж': 'Zh', + 'З': 'Z', + 'И': 'I', + 'Й': 'I', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'Kh', + 'Ц': 'Ts', + 'Ч': 'Ch', + 'Ш': 'Sh', + 'Щ': 'Shch', + 'Ъ': '""', + 'Ы': 'Y', + 'Ь': '\'\'', + 'Э': 'E', + 'Ю': 'Iu', + 'Я': 'Ia' + }, + + // BGN + 'bgn': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'e', + 'ж': 'zh', + 'з': 'z', + 'и': 'i', + 'й': 'y', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'kh', + 'ц': 'ts', + 'ч': 'ch', + 'ш': 'sh', + 'щ': 'shch', + 'ъ': '"', + 'ы': 'y', + 'ь': '\'', + 'э': 'e', + 'ю': 'yu', + 'я': 'ya', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'E', + 'Ж': 'Zh', + 'З': 'Z', + 'И': 'I', + 'Й': 'Y', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'Kh', + 'Ц': 'Ts', + 'Ч': 'Ch', + 'Ш': 'Sh', + 'Щ': 'Shch', + 'Ъ': '""', + 'Ы': 'Y', + 'Ь': '\'\'', + 'Э': 'E', + 'Ю': 'Yu', + 'Я': 'Ya' + }, + + // BSI + 'bsi': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'e', + 'ж': 'zh', + 'з': 'z', + 'и': 'i', + 'й': 'i', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'kh', + 'ц': 'ts', + 'ч': 'ch', + 'ш': 'sh', + 'щ': 'shch', + 'ъ': '"', + 'ы': 'y', + 'ь': '\'', + 'э': 'e', + 'ю': 'yu', + 'я': 'ya', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'E', + 'Ж': 'Zh', + 'З': 'Z', + 'И': 'I', + 'Й': 'I', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'Kh', + 'Ц': 'Ts', + 'Ч': 'Ch', + 'Ш': 'Sh', + 'Щ': 'Shch', + 'Ъ': '""', + 'Ы': 'Y', + 'Ь': '\'\'', + 'Э': 'E', + 'Ю': 'Yu', + 'Я': 'Ya' + }, + + // Сходно с МВД + 'police': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'e', + 'ж': 'zh', + 'з': 'z', + 'и': 'i', + 'й': 'i', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'kh', + 'ц': 'ts', + 'ч': 'ch', + 'ш': 'sh', + 'щ': 'shch', + 'ъ': 'ie', + 'ы': 'y', + 'ь': '', + 'э': 'e', + 'ю': 'iu', + 'я': 'ia', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'E', + 'Ж': 'Zh', + 'З': 'Z', + 'И': 'I', + 'Й': 'I', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'Kh', + 'Ц': 'Ts', + 'Ч': 'Ch', + 'Ш': 'Sh', + 'Щ': 'Shch', + 'Ъ': 'Ie', + 'Ы': 'Y', + 'Ь': '', + 'Э': 'E', + 'Ю': 'Iu', + 'Я': 'Ia' + }, + + // Как на загранпаспорт + 'foreign-passport': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'e', + 'ж': 'zh', + 'з': 'z', + 'и': 'i', + 'й': 'i', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'kh', + 'ц': 'tc', + 'ч': 'ch', + 'ш': 'sh', + 'щ': 'shch', + 'ъ': '', + 'ы': 'y', + 'ь': '', + 'э': 'e', + 'ю': 'iu', + 'я': 'ia', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'E', + 'Ж': 'Zh', + 'З': 'Z', + 'И': 'I', + 'Й': 'I', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'Kh', + 'Ц': 'Tc', + 'Ч': 'Ch', + 'Ш': 'Sh', + 'Щ': 'Shch', + 'Ъ': '', + 'Ы': 'Y', + 'Ь': '', + 'Э': 'E', + 'Ю': 'Iu', + 'Я': 'Ia' + }, + + // Международные телеграммы + 'international-telegrams': { + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ё': 'e', + 'ж': 'j', + 'з': 'z', + 'и': 'i', + 'й': 'i', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'h', + 'ц': 'c', + 'ч': 'ch', + 'ш': 'sh', + 'щ': 'sc', + 'ъ': '', + 'ы': 'y', + 'ь': '', + 'э': 'e', + 'ю': 'iu', + 'я': 'ia', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ё': 'E', + 'Ж': 'J', + 'З': 'Z', + 'И': 'I', + 'Й': 'I', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'H', + 'Ц': 'C', + 'Ч': 'Ch', + 'Ш': 'Sh', + 'Щ': 'Sc', + 'Ъ': '', + 'Ы': 'Y', + 'Ь': '', + 'Э': 'E', + 'Ю': 'Iu', + 'Я': 'Ia' + } + }; + + function toYandexFriendlyUrl(value) { + var processedValue, + result + ; + + processedValue = value.toLowerCase(); + result = processedValue.replace(/([а-яё])|([\s_-])|([^a-z\d])/gi, + function (all, charValue, space, special, offset) { + var replacements, + charCode, + index, + transliteratedCharValue + ; + + if (space) { + return '-'; + } + + if (special) { + return ''; + } + + replacements = ['yo', 'a', 'b', 'v', 'g', 'd', 'e', 'zh', + 'z', 'i', 'y', 'k', 'l', 'm', 'n', 'o', 'p', + 'r', 's', 't', 'u', 'f', 'h', 'c', 'ch', 'sh', + 'shch', '', 'y', '', 'e', 'yu', 'ya']; + + charCode = charValue.charCodeAt(0); + if (charCode == 1025 || charCode == 1105) { + index = 0; + } + else { + index = charCode > 1071 ? charCode - 1071 : charCode - 1039; + } + + transliteratedCharValue = replacements[index]; + + return transliteratedCharValue; + } + ); + + return result; + } + + /** + * Производит транслитерацию русского текста с кириллицы на латиницу + * + * @param {String} value - Текст, содержащий символы русского (кириллического) алфавита + * @param {String} type - Код типа (системы) транслитерации + * @returns {String} Текст, содержащий только символы латинского алфавита + * @expose + */ + function transliterate(value, type) { + var charCount, + charIndex, + charValue, + newCharValue, + characterMapping, + result + ; + + value = value || ''; + type = type || 'basic'; + + charCount = value.length; + if (charCount === 0) { + return value; + } + + if (type === 'yandex-friendly-url') { + return toYandexFriendlyUrl(value); + } + + characterMapping = characterMappings[type]; + if (typeof characterMapping === 'undefined') { + return value; + } + + result = ''; + + for (charIndex = 0; charIndex < charCount; charIndex++) { + charValue = value.charAt(charIndex); + newCharValue = typeof characterMapping[charValue] !== 'undefined' ? + characterMapping[charValue] : charValue; + result += newCharValue; + } + + return result; + } + + return transliterate; +}()); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Benchmarks/bundleconfig.json b/test/JavaScriptEngineSwitcher.Benchmarks/bundleconfig.json new file mode 100644 index 00000000..71216504 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Benchmarks/bundleconfig.json @@ -0,0 +1,9 @@ +[ + { + "outputFileName": "Files/template-rendering/lib/bundle.min.js", + "inputFiles": [ + "Files/template-rendering/lib/handlebars.js", + "Files/template-rendering/lib/helpers.js" + ] + } +] diff --git a/test/JavaScriptEngineSwitcher.Tests/App.config b/test/JavaScriptEngineSwitcher.Tests/App.config deleted file mode 100644 index 70b1f8e7..00000000 --- a/test/JavaScriptEngineSwitcher.Tests/App.config +++ /dev/null @@ -1,44 +0,0 @@ - - - - -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/ChakraCore/CommonTests.cs b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/CommonTests.cs index a0963b60..fd1ddb64 100644 --- a/test/JavaScriptEngineSwitcher.Tests/ChakraCore/CommonTests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/CommonTests.cs @@ -1,14 +1,351 @@ -namespace JavaScriptEngineSwitcher.Tests.ChakraCore -{ - using Core; +using System; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.ChakraCore; +namespace JavaScriptEngineSwitcher.Tests.ChakraCore +{ public class CommonTests : CommonTestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName + { + get { return "ChakraCoreJsEngine"; } + } + + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Invalid character", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(5, exception.ColumnNumber); + Assert.Equal("var @variable3 = 678;", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var variable3 = 678; + +$variable1 + -variable2 - variable3;"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("'variable2' is not defined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(5, exception.LineNumber); + Assert.Equal(1, exception.ColumnNumber); + Assert.Equal("$variable1 + -variable2 - variable3;", exception.SourceFragment); + Assert.Equal(" at Global code (variables.js:5:1)", exception.CallStack); + } + + [Fact] + public void MappingCompilationErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(2%); +factorial(0);"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token ')' after '%'", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(13, exception.ColumnNumber); + Assert.Equal("factorial(2%);", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(-1); +factorial(0);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The value must be greater than or equal to zero.", exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(3, exception.ColumnNumber); + Assert.Equal( + " throw new Error(\"The value must be greater than or equal to zero.\");", + exception.SourceFragment + ); + Assert.Equal( + " at factorial (factorial.js:3:3)" + Environment.NewLine + + " at Global code (factorial.js:10:1)", + exception.CallStack + ); + } + + [Fact] + public void MappingRuntimeErrorDuringOutOfMemory() + { + // Arrange + const string input = @"var arr = []; + +for (var i = 0; i < 10000; i++) { + arr.push('Current date: ' + new Date()); +}"; + + // Act + IJsEngine jsEngine = null; + JsException exception = null; + + try + { + jsEngine = new ChakraCoreJsEngine( + new ChakraCoreSettings + { + MemoryLimit = new UIntPtr(2 * 1024 * 1024) + } + ); + jsEngine.Execute(input); + } + catch (JsEngineException e) + { + exception = e; + } + catch (JsRuntimeException e) + { + exception = e; + } + finally + { + jsEngine?.Dispose(); + } + + // Assert + Assert.NotNull(exception); + if (exception is JsRuntimeException) + { + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Out of memory", exception.Description); + } + else if (exception is JsEngineException) + { + Assert.Equal("Engine load error", exception.Category); + Assert.Equal("Out of memory.", exception.Description); + } + } + + [Fact] + public void MappingEngineLoadErrorDuringOutOfMemory() { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("ChakraCoreJsEngine"); + // Arrange - return jsEngine; + // Act + IJsEngine jsEngine = null; + JsEngineLoadException exception = null; + + try + { + jsEngine = new ChakraCoreJsEngine( + new ChakraCoreSettings + { + MemoryLimit = new UIntPtr(8 * 1024) + } + ); + } + catch (JsEngineLoadException e) + { + exception = e; + } + finally + { + jsEngine?.Dispose(); + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Engine load error", exception.Category); + Assert.Equal("Out of memory.", exception.Description); } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"var arr = []; +var obj = {}; +var foo = 'Browser's bar';"; + string targetOutput = "SyntaxError: Expected ';'" + Environment.NewLine + + " at variables.js:3:20 -> var foo = 'Browser's bar';" + ; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function foo(x, y) { + var z = x + y; + if (z > 20) { + bar(); + } +} + +(function (foo) { + var a = 8; + var b = 15; + + foo(a, b); +})(foo);"; + string targetOutput = "ReferenceError: 'bar' is not defined" + Environment.NewLine + + " at foo (functions.js:4:3) -> bar();" + Environment.NewLine + + " at Anonymous function (functions.js:12:2)" + Environment.NewLine + + " at Global code (functions.js:8:2)" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "functions.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/ChakraCore/Es2015Tests.cs b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/Es2015Tests.cs new file mode 100644 index 00000000..cad4449c --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/Es2015Tests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.ChakraCore +{ + public class Es2015Tests : Es2015TestsBase + { + protected override string EngineName + { + get { return "ChakraCoreJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/ChakraCore/Es5Tests.cs b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/Es5Tests.cs index ab8090d0..adc6ecba 100644 --- a/test/JavaScriptEngineSwitcher.Tests/ChakraCore/Es5Tests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/Es5Tests.cs @@ -1,14 +1,10 @@ namespace JavaScriptEngineSwitcher.Tests.ChakraCore { - using Core; - public class Es5Tests : Es5TestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("ChakraCoreJsEngine"); - - return jsEngine; + get { return "ChakraCoreJsEngine"; } } } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/ChakraCore/EvalTests.cs b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/EvalTests.cs new file mode 100644 index 00000000..2e4b9de4 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/EvalTests.cs @@ -0,0 +1,65 @@ +using Xunit; + +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests.ChakraCore +{ + public class EvalTests : EvalTestsBase + { + protected override string EngineName + { + get { return "ChakraCoreJsEngine"; } + } + + + private IJsEngine CreateJsEngine(bool disableEval) + { + var jsEngine = new ChakraCoreJsEngine(new ChakraCoreSettings + { + DisableEval = disableEval + }); + + return jsEngine; + } + + + public override void UsageOfEvalFunction() + { + // Arrange + int TestDisableEvalSetting(bool disableEval) + { + using (var jsEngine = CreateJsEngine(disableEval: disableEval)) + { + return jsEngine.Evaluate("eval('2*2');"); + } + } + + // Act and Assert + Assert.Equal(4, TestDisableEvalSetting(false)); + + JsRuntimeException exception = Assert.Throws(() => TestDisableEvalSetting(true)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Eval of strings is disabled in this runtime.", exception.Description); + } + + public override void UsageOfFunctionConstructor() + { + // Arrange + int TestDisableEvalSetting(bool disableEval) + { + using (var jsEngine = CreateJsEngine(disableEval: disableEval)) + { + return jsEngine.Evaluate("new Function('return 2*2;')();"); + } + } + + // Act and Assert + Assert.Equal(4, TestDisableEvalSetting(false)); + + JsRuntimeException exception = Assert.Throws(() => TestDisableEvalSetting(true)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Eval of strings is disabled in this runtime.", exception.Description); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/ChakraCore/InteropTests.cs b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/InteropTests.cs index 38e18c4c..7aa231dc 100644 --- a/test/JavaScriptEngineSwitcher.Tests/ChakraCore/InteropTests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/InteropTests.cs @@ -1,14 +1,513 @@ -namespace JavaScriptEngineSwitcher.Tests.ChakraCore -{ - using Core; +using System; +using System.IO; +using System.Reflection; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.ChakraCore; + +using JavaScriptEngineSwitcher.Tests.Interop; +using JavaScriptEngineSwitcher.Tests.Interop.Animals; +namespace JavaScriptEngineSwitcher.Tests.ChakraCore +{ public class InteropTests : InteropTestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("ChakraCoreJsEngine"); + get { return "ChakraCoreJsEngine"; } + } + + + private IJsEngine CreateJsEngine(bool allowReflection) + { + var jsEngine = new ChakraCoreJsEngine(new ChakraCoreSettings + { + AllowReflection = allowReflection + }); return jsEngine; } + + #region Embedding of objects + + #region Objects with methods + + [Fact] + public override void EmbeddingOfInstanceOfCustomValueTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var date = new Date(); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("date", date); + return jsEngine.Evaluate("date.GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(Date).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Object doesn't support property or method 'GetType'", exception.Description); + } + + [Fact] + public override void EmbeddingOfInstanceOfCustomReferenceTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var cat = new Cat(); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("cat", cat); + return jsEngine.Evaluate("cat.GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(Cat).FullName, TestAllowReflectionSetting(true)); + + JsRuntimeException exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Object doesn't support property or method 'GetType'", exception.Description); + } + + [Fact] + public override void EmbeddingOfInstanceOfAssemblyTypeAndCallingOfItsCreateInstanceMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Assembly assembly = this.GetType().Assembly; + string personTypeName = typeof(Person).FullName; + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("assembly", assembly); + return jsEngine.Evaluate("assembly.CreateInstance(\"" + personTypeName + "\");"); + } + } + + // Act and Assert + Assert.Equal("{FirstName=,LastName=}", TestAllowReflectionSetting(true)); + Assert.Equal("{FirstName=,LastName=}", TestAllowReflectionSetting(false)); + } + + #endregion + + #region Delegates + + [Fact] + public override void EmbeddingOfInstanceOfDelegateAndGettingItsMethodProperty() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var cat = new Cat(); + var cryFunc = new Func(cat.Cry); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("cry", cryFunc); + return jsEngine.Evaluate("cry.Method;"); + } + } + + // Act and Assert + Assert.Equal("undefined", TestAllowReflectionSetting(true)); + Assert.Equal("undefined", TestAllowReflectionSetting(false)); + } + + #endregion + + #region Recursive calls + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringRecursiveEvaluationOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-evaluation/compilation-error"; + const string input = "evaluateFile('index').calculateResult();"; + + // Act + JsCompilationException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Func evaluateFile = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("evaluateFile", evaluateFile); + double output = jsEngine.Evaluate(input); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Expected identifier", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("math.js", exception.DocumentName); + Assert.Equal(25, exception.LineNumber); + Assert.Equal(11, exception.ColumnNumber); + Assert.Equal(" PI: 3,14,", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringRecursiveEvaluationOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-evaluation/runtime-error"; + const string input = "evaluateFile('index').calculateResult();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Func evaluateFile = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("evaluateFile", evaluateFile); + double output = jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("'argumens' is not defined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("math.js", exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(4, exception.ColumnNumber); + Assert.Equal(" result += argumens[i];", exception.SourceFragment); + Assert.Equal( + " at sum (math.js:10:4)" + Environment.NewLine + + " at calculateResult (index.js:7:4)" + Environment.NewLine + + " at Global code (Script Document:1:1)", + exception.CallStack + ); + } + + [Fact] + public void MappingHostErrorDuringRecursiveEvaluationOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-evaluation/host-error"; + const string input = "evaluateFile('index').calculateResult();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Func evaluateFile = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("evaluateFile", evaluateFile); + double output = jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.StartsWith("During invocation of the host delegate an error has occurred - ", + exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Equal("index.js", exception.DocumentName); + Assert.Equal(6, exception.LineNumber); + Assert.Equal(3, exception.ColumnNumber); + Assert.Equal(" var math = evaluateFile('./match'),", exception.SourceFragment); + Assert.Equal( + " at calculateResult (index.js:6:3)" + Environment.NewLine + + " at Global code (Script Document:1:1)", + exception.CallStack + ); + } + + [Fact] + public void MappingCompilationErrorDuringRecursiveExecutionOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-execution/compilation-error"; + const string variableName = "num"; + + // Act + JsCompilationException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Action executeFile = path => jsEngine.ExecuteFile(path); + + jsEngine.SetVariableValue("directoryPath", directoryPath); + jsEngine.EmbedHostObject("executeFile", executeFile); + jsEngine.ExecuteFile(Path.Combine(directoryPath, "main-file.js")); + + int output = jsEngine.GetVariableValue(variableName); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Invalid character", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("second-file.js", exception.DocumentName); + Assert.Equal(1, exception.LineNumber); + Assert.Equal(6, exception.ColumnNumber); + Assert.Equal("num -# 3;", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringRecursiveExecutionOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-execution/runtime-error"; + const string variableName = "num"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Action executeFile = path => jsEngine.ExecuteFile(path); + + jsEngine.SetVariableValue("directoryPath", directoryPath); + jsEngine.EmbedHostObject("executeFile", executeFile); + jsEngine.ExecuteFile(Path.Combine(directoryPath, "main-file.js")); + + int output = jsEngine.GetVariableValue(variableName); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("'nuм' is not defined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("second-file.js", exception.DocumentName); + Assert.Equal(1, exception.LineNumber); + Assert.Equal(1, exception.ColumnNumber); + Assert.Equal("nuм -= 3;", exception.SourceFragment); + Assert.Equal( + " at Global code (second-file.js:1:1)" + Environment.NewLine + + " at Global code (first-file.js:2:1)" + Environment.NewLine + + " at Global code (main-file.js:2:1)", + exception.CallStack + ); + } + + [Fact] + public void MappingHostErrorDuringRecursiveExecutionOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-execution/host-error"; + const string variableName = "num"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Action executeFile = path => jsEngine.ExecuteFile(path); + + jsEngine.SetVariableValue("directoryPath", directoryPath); + jsEngine.EmbedHostObject("executeFile", executeFile); + jsEngine.ExecuteFile(Path.Combine(directoryPath, "main-file.js")); + + int output = jsEngine.GetVariableValue(variableName); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.StartsWith( + "During invocation of the host delegate an error has occurred - ", + exception.Description + ); + Assert.Equal("Error", exception.Type); + Assert.Equal("first-file.js", exception.DocumentName); + Assert.Equal(2, exception.LineNumber); + Assert.Equal(1, exception.ColumnNumber); + Assert.Equal( + "executeFile(directoryPath + \"/second-file.jsx\");", + exception.SourceFragment + ); + Assert.Equal( + " at Global code (first-file.js:2:1)" + Environment.NewLine + + " at Global code (main-file.js:2:1)", + exception.CallStack + ); + } + + #endregion + + #endregion + + #endregion + + + #region Embedding of types + + #region Creating of instances + + [Fact] + public override void CreatingAnInstanceOfEmbeddedBuiltinExceptionAndGettingItsTargetSiteProperty() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Type invalidOperationExceptionType = typeof(InvalidOperationException); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("InvalidOperationError", invalidOperationExceptionType); + return jsEngine.Evaluate("new InvalidOperationError(\"A terrible thing happened!\").TargetSite;"); + } + } + + // Act and Assert + Assert.Null(TestAllowReflectionSetting(true)); + Assert.Equal("undefined", TestAllowReflectionSetting(false)); + } + + [Fact] + public override void CreatingAnInstanceOfEmbeddedCustomExceptionAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Type loginFailedExceptionType = typeof(LoginFailedException); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("LoginFailedError", loginFailedExceptionType); + return jsEngine.Evaluate("new LoginFailedError(\"Wrong password entered!\").GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(LoginFailedException).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Object doesn't support property or method 'GetType'", exception.Description); + } + + #endregion + + #region Types with methods + + [Fact] + public override void EmbeddingOfTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string dateTimeTypeName = typeof(DateTime).FullName; + + string TestAllowReflectionSetting(bool allowReflection) + { + Type type = typeof(Type); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("Type", type); + return jsEngine.Evaluate("Type.GetType(\"" + dateTimeTypeName + "\");"); + } + } + + // Act and Assert + Assert.Equal(dateTimeTypeName, TestAllowReflectionSetting(true)); + Assert.Equal(dateTimeTypeName, TestAllowReflectionSetting(false)); + } + + [Fact] + public override void EmbeddingOfAssemblyTypeAndCallingOfItsLoadMethod() + { + // Arrange + const string reflectionEmitAssemblyName = "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + + string TestAllowReflectionSetting(bool allowReflection) + { + Type assemblyType = typeof(Assembly); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("Assembly", assemblyType); + return jsEngine.Evaluate("Assembly.Load(\"" + reflectionEmitAssemblyName + "\");"); + } + } + + // Act and Assert + Assert.Equal(reflectionEmitAssemblyName, TestAllowReflectionSetting(true)); + Assert.Equal(reflectionEmitAssemblyName, TestAllowReflectionSetting(false)); + } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/ChakraCore/MultithreadingTests.cs b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/MultithreadingTests.cs new file mode 100644 index 00000000..df2acc6b --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/MultithreadingTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.ChakraCore +{ + public class MultithreadingTests : MultithreadingTestsBase + { + protected override string EngineName + { + get { return "ChakraCoreJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/ChakraCore/PrecompilationTests.cs b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/PrecompilationTests.cs new file mode 100644 index 00000000..55a631d4 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/ChakraCore/PrecompilationTests.cs @@ -0,0 +1,210 @@ +using System; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests.ChakraCore +{ + public class PrecompilationTests : PrecompilationTestsBase + { + protected override string EngineName + { + get { return "ChakraCoreJsEngine"; } + } + + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringPrecompilationOfCode() + { + // Arrange + const string input = @"function guid() { + function s4() { + return Math.floor((1 + Math.random() * 0x10000) + .toString(16) + .substring(1) + ; + } + + var result = s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + + return result; +}"; + + IPrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "guid.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Expected ')'", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("guid.js", exception.DocumentName); + Assert.Equal(6, exception.LineNumber); + Assert.Equal(4, exception.ColumnNumber); + Assert.Equal(" ;", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfPrecompiledCode() + { + // Arrange + const string input = @"function getItem(items, itemIndex) { + var item = items[itemIndex]; + + return item; +} + +(function (getItem) { + var items = null, + item = getItem(items, 5) + ; + + return item; +})(getItem);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + IPrecompiledScript precompiledScript = jsEngine.Precompile(input, "get-item.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Unable to get property '5' of undefined or null reference", exception.Description); + Assert.Equal("TypeError", exception.Type); + Assert.Equal("get-item.js", exception.DocumentName); + Assert.Equal(2, exception.LineNumber); + Assert.Equal(2, exception.ColumnNumber); + Assert.Equal(" var item = items[itemIndex];", exception.SourceFragment); + Assert.Equal( + " at getItem (get-item.js:2:2)" + Environment.NewLine + + " at Anonymous function (get-item.js:9:3)" + Environment.NewLine + + " at Global code (get-item.js:7:2)", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"function makeId(length) { + var result = '', + possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + charIndex + ; + + for (charIndex = 0; charIndex < length; charIndex++) + result += possible.charAt(Math.floor(Math.random() * possible.length)); + } + + return result; +}"; + string targetOutput = "SyntaxError: 'return' statement outside of function" + Environment.NewLine + + " at make-id.js:11:2 -> return result;" + ; + + IPrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "make-id.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function getFullName(firstName, lastName) { + var fullName = firstName + ' ' + middleName + ' ' + lastName; + + return fullName; +} + +(function (getFullName) { + var firstName = 'Vasya', + lastName = 'Pupkin' + ; + + return getFullName(firstName, lastName); +})(getFullName);"; + string targetOutput = "ReferenceError: 'middleName' is not defined" + Environment.NewLine + + " at getFullName (get-full-name.js:2:2) -> var fullName = firstName + ' ' + middleName + " + + "' ' + lastName;" + Environment.NewLine + + " at Anonymous function (get-full-name.js:12:2)" + Environment.NewLine + + " at Global code (get-full-name.js:7:2)" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + IPrecompiledScript precompiledScript = jsEngine.Precompile(input, "get-full-name.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/CommonTestsBase.cs b/test/JavaScriptEngineSwitcher.Tests/CommonTestsBase.cs index 0df4ba9f..a3e6edbd 100644 --- a/test/JavaScriptEngineSwitcher.Tests/CommonTestsBase.cs +++ b/test/JavaScriptEngineSwitcher.Tests/CommonTestsBase.cs @@ -1,21 +1,30 @@ -namespace JavaScriptEngineSwitcher.Tests -{ - using System; - using System.IO; - using System.Reflection; +using System; +using System.Reflection; +using System.Threading; - using NUnit.Framework; +using Xunit; - using Core; +using JavaScriptEngineSwitcher.Core; - public abstract class CommonTestsBase : FileSystemTestsBase +namespace JavaScriptEngineSwitcher.Tests +{ + public abstract class CommonTestsBase : TestsBase { - protected abstract IJsEngine CreateJsEngine(); + #region Creation of engines + + [Fact] + public virtual void CreationOfEngineWithoutDisposing() + { + IJsEngine jsEngine = CreateJsEngine(); + jsEngine.Execute("var a = 1 + 1;"); + } + + #endregion - #region Evaluation of code + #region Evaluation of scripts - [Test] - public virtual void EvaluationOfExpressionWithUndefinedResultIsCorrect() + [Fact] + public virtual void EvaluationOfExpressionWithUndefinedResult() { // Arrange const string input = "undefined"; @@ -30,11 +39,11 @@ public virtual void EvaluationOfExpressionWithUndefinedResultIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EvaluationOfExpressionWithNullResultIsCorrect() + [Fact] + public virtual void EvaluationOfExpressionWithNullResult() { // Arrange const string input = "null"; @@ -49,11 +58,11 @@ public virtual void EvaluationOfExpressionWithNullResultIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EvaluationOfExpressionWithBooleanResultIsCorrect() + [Fact] + public virtual void EvaluationOfExpressionWithBooleanResult() { // Arrange const string input1 = "7 > 5"; @@ -73,12 +82,12 @@ public virtual void EvaluationOfExpressionWithBooleanResultIsCorrect() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); } - [Test] - public virtual void EvaluationOfExpressionWithIntegerResultIsCorrect() + [Fact] + public virtual void EvaluationOfExpressionWithIntegerResult() { // Arrange const string input = "7 * 8 - 20"; @@ -93,11 +102,11 @@ public virtual void EvaluationOfExpressionWithIntegerResultIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EvaluationOfExpressionWithDoubleResultIsCorrect() + [Fact] + public virtual void EvaluationOfExpressionWithDoubleResult() { // Arrange const string input = "Math.PI + 0.22"; @@ -112,11 +121,11 @@ public virtual void EvaluationOfExpressionWithDoubleResultIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EvaluationOfExpressionWithStringResultIsCorrect() + [Fact] + public virtual void EvaluationOfExpressionWithStringResult() { // Arrange const string input = "'Hello, ' + \"Vasya\" + '?';"; @@ -131,15 +140,34 @@ public virtual void EvaluationOfExpressionWithStringResultIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void EvaluationOfExpressionWithUnicodeStringResult() + { + // Arrange + const string input = "'Привет, ' + \"Вася\" + '?';"; + const string targetOutput = "Привет, Вася?"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); } #endregion - #region Execution of code + #region Execution of scripts - [Test] - public virtual void ExecutionOfCodeIsCorrect() + [Fact] + public virtual void ExecutionOfCode() { // Arrange const string functionCode = @"function add(num1, num2) { @@ -158,14 +186,14 @@ public virtual void ExecutionOfCodeIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void ExecutionOfFileIsCorrect() + [Fact] + public virtual void ExecutionOfFile() { // Arrange - string filePath = Path.GetFullPath(Path.Combine(_baseDirectoryPath, "JavaScriptEngineSwitcher.Tests/Files/square.js")); + const string filePath = "Files/square.js"; const string input = "square(6);"; const int targetOutput = 36; @@ -179,14 +207,14 @@ public virtual void ExecutionOfFileIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void ExecutionOfResourceByTypeIsCorrect() + [Fact] + public virtual void ExecutionOfResourceByNameAndType() { // Arrange - const string resourceName = "JavaScriptEngineSwitcher.Tests.Resources.cube.js"; + const string resourceName = "Resources.cube.js"; const string input = "cube(5);"; const int targetOutput = 125; @@ -195,16 +223,16 @@ public virtual void ExecutionOfResourceByTypeIsCorrect() using (var jsEngine = CreateJsEngine()) { - jsEngine.ExecuteResource(resourceName, GetType()); + jsEngine.ExecuteResource(resourceName, typeof(CommonTestsBase)); output = jsEngine.Evaluate(input); } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void ExecutionOfResourceByAssemblyIsCorrect() + [Fact] + public virtual void ExecutionOfResourceByNameAndAssembly() { // Arrange const string resourceName = "JavaScriptEngineSwitcher.Tests.Resources.power.js"; @@ -216,20 +244,20 @@ public virtual void ExecutionOfResourceByAssemblyIsCorrect() using (var jsEngine = CreateJsEngine()) { - jsEngine.ExecuteResource(resourceName, Assembly.GetExecutingAssembly()); + jsEngine.ExecuteResource(resourceName, typeof(CommonTestsBase).GetTypeInfo().Assembly); output = jsEngine.Evaluate(input); } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } #endregion #region Calling of functions - [Test] - public void CallingOfFunctionWithoutParametersIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithoutParameters() { // Arrange const string functionCode = @"function hooray() { @@ -247,11 +275,11 @@ public void CallingOfFunctionWithoutParametersIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void CallingOfFunctionWithUndefinedResultIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithUndefinedResult() { // Arrange const string functionCode = @"function testUndefined(value) { @@ -273,11 +301,11 @@ public virtual void CallingOfFunctionWithUndefinedResultIsCorrect() } // Assert - Assert.AreEqual(input, output); + Assert.Equal(input, output); } - [Test] - public virtual void CallingOfFunctionWithNullResultIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithNullResult() { // Arrange const string functionCode = @"function testNull(value) { @@ -299,11 +327,11 @@ public virtual void CallingOfFunctionWithNullResultIsCorrect() } // Assert - Assert.AreEqual(input, output); + Assert.Equal(input, output); } - [Test] - public virtual void CallingOfFunctionWithBooleanResultIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithBooleanResult() { // Arrange const string functionCode = @"function inverse(value) { @@ -322,11 +350,11 @@ public virtual void CallingOfFunctionWithBooleanResultIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void CallingOfFunctionWithIntegerResultIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithIntegerResult() { // Arrange const string functionCode = @"function negate(value) { @@ -345,11 +373,11 @@ public virtual void CallingOfFunctionWithIntegerResultIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void CallingOfFunctionWithDoubleResultIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithDoubleResult() { // Arrange const string functionCode = @"function triple(value) { @@ -368,11 +396,11 @@ public virtual void CallingOfFunctionWithDoubleResultIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void CallingOfFunctionWithStringResultIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithStringResult() { // Arrange const string functionCode = @"function greeting(name) { @@ -391,11 +419,34 @@ public virtual void CallingOfFunctionWithStringResultIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void CallingOfFunctionWithManyParametersIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithUnicodeStringResult() + { + // Arrange + const string functionCode = @"function privet(name) { + return 'Привет, ' + name + '!'; +}"; + const string input = "Вован"; + const string targetOutput = "Привет, Вован!"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(functionCode); + output = jsEngine.CallFunction("privet", input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void CallingOfFunctionWithManyParameters() { // Arrange const string functionCode = @"function determineArgumentsTypes() { @@ -425,11 +476,11 @@ public virtual void CallingOfFunctionWithManyParametersIsCorrect() } // Assert - Assert.AreEqual("undefined, object, boolean, number, number, string", output); + Assert.Equal("undefined, object, boolean, number, number, string", output); } - [Test] - public virtual void CallingOfFunctionWithManyParametersAndBooleanResultIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithManyParametersAndBooleanResult() { // Arrange const string functionCode = @"function and() { @@ -463,11 +514,11 @@ public virtual void CallingOfFunctionWithManyParametersAndBooleanResultIsCorrect } // Assert - Assert.AreEqual(false, output); + Assert.False(output); } - [Test] - public virtual void CallingOfFunctionWithManyParametersAndIntegerResultIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithManyParametersAndIntegerResult() { // Arrange const string functionCode = @"function sum() { @@ -493,11 +544,11 @@ public virtual void CallingOfFunctionWithManyParametersAndIntegerResultIsCorrect } // Assert - Assert.AreEqual(206, output); + Assert.Equal(206, output); } - [Test] - public virtual void CallingOfFunctionWithManyParametersAndDoubleResultIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithManyParametersAndDoubleResult() { // Arrange const string functionCode = @"function sum() { @@ -523,11 +574,11 @@ public virtual void CallingOfFunctionWithManyParametersAndDoubleResultIsCorrect( } // Assert - Assert.AreEqual(22011.55, output); + Assert.Equal(22011.55, output); } - [Test] - public virtual void CallingOfFunctionWithManyParametersAndStringResultIsCorrect() + [Fact] + public virtual void CallingOfFunctionWithManyParametersAndStringResult() { // Arrange const string functionCode = @"function concatenate() { @@ -553,15 +604,68 @@ public virtual void CallingOfFunctionWithManyParametersAndStringResultIsCorrect( } // Assert - Assert.AreEqual("Hello, Petya!", output); + Assert.Equal("Hello, Petya!", output); + } + + [Fact] + public virtual void CallingOfFunctionWithManyParametersAndUnicodeStringResult() + { + // Arrange + const string functionCode = @"function obedinit() { + var result = '', + argumentIndex, + argumentCount = arguments.length + ; + + for (argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) { + result += arguments[argumentIndex]; + } + + return result; +}"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(functionCode); + output = jsEngine.CallFunction("obedinit", "Привет", ",", " ", "Петя", "!"); + } + + // Assert + Assert.Equal("Привет, Петя!", output); + } + + [Fact] + public virtual void CallingOfFunctionWithNameContainingUnicodeCharacters() + { + // Arrange + const string functionCode = @"function сумма(число1, число2) { + var результат = число1 + число2; + + return результат; +}"; + + // Act + int output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(functionCode); + output = jsEngine.CallFunction("сумма", 678, 711); + } + + // Assert + Assert.Equal(1389, output); } #endregion #region Getting, setting and removing variables - [Test] - public virtual void SettingAndGettingVariableWithUndefinedValueIsCorrect() + [Fact] + public virtual void SettingAndGettingVariableWithUndefinedValue() { // Arrange const string variableName = "myVar1"; @@ -579,12 +683,12 @@ public virtual void SettingAndGettingVariableWithUndefinedValueIsCorrect() } // Assert - Assert.IsFalse(variableExists); - Assert.AreEqual(input, output); + Assert.False(variableExists); + Assert.Equal(input, output); } - [Test] - public virtual void SettingAndGettingVariableWithNullValueIsCorrect() + [Fact] + public virtual void SettingAndGettingVariableWithNullValue() { // Arrange const string variableName = "myVar2"; @@ -602,12 +706,12 @@ public virtual void SettingAndGettingVariableWithNullValueIsCorrect() } // Assert - Assert.IsTrue(variableExists); - Assert.AreEqual(input, output); + Assert.True(variableExists); + Assert.Equal(input, output); } - [Test] - public virtual void SettingAndGettingVariableWithBooleanValueIsCorrect() + [Fact] + public virtual void SettingAndGettingVariableWithBooleanValue() { // Arrange const string variableName = "isVisible"; @@ -634,14 +738,14 @@ public virtual void SettingAndGettingVariableWithBooleanValueIsCorrect() } // Assert - Assert.IsTrue(variableExists); - Assert.AreEqual(targetOutput1, output1); + Assert.True(variableExists); + Assert.Equal(targetOutput1, output1); - Assert.AreEqual(input2, output2); + Assert.Equal(input2, output2); } - [Test] - public virtual void SettingAndGettingVariableWithIntegerValueIsCorrect() + [Fact] + public virtual void SettingAndGettingVariableWithIntegerValue() { // Arrange const string variableName = "amount"; @@ -668,14 +772,14 @@ public virtual void SettingAndGettingVariableWithIntegerValueIsCorrect() } // Assert - Assert.IsTrue(variableExists); - Assert.AreEqual(targetOutput1, output1); + Assert.True(variableExists); + Assert.Equal(targetOutput1, output1); - Assert.AreEqual(input2, output2); + Assert.Equal(input2, output2); } - [Test] - public virtual void SettingAndGettingVariableWithDoubleValueIsCorrect() + [Fact] + public virtual void SettingAndGettingVariableWithDoubleValue() { // Arrange const string variableName = "price"; @@ -702,14 +806,14 @@ public virtual void SettingAndGettingVariableWithDoubleValueIsCorrect() } // Assert - Assert.IsTrue(variableExists); - Assert.AreEqual(targetOutput1, output1); + Assert.True(variableExists); + Assert.Equal(targetOutput1, output1); - Assert.AreEqual(input2, output2); + Assert.Equal(input2, output2); } - [Test] - public virtual void SettingAndGettingVariableWithStringValueIsCorrect() + [Fact] + public virtual void SettingAndGettingVariableWithStringValue() { // Arrange const string variableName = "word"; @@ -736,14 +840,82 @@ public virtual void SettingAndGettingVariableWithStringValueIsCorrect() } // Assert - Assert.IsTrue(variableExists); - Assert.AreEqual(targetOutput1, output1); + Assert.True(variableExists); + Assert.Equal(targetOutput1, output1); - Assert.AreEqual(input2, output2); + Assert.Equal(input2, output2); } - [Test] - public virtual void RemovingVariableIsCorrect() + [Fact] + public virtual void SettingAndGettingVariableWithUnicodeStringValue() + { + // Arrange + const string variableName = "slovo"; + + const string input1 = "Ура"; + const string targetOutput1 = "Ура!"; + + const string input2 = "Урааа"; + + // Act + bool variableExists; + string output1; + string output2; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.SetVariableValue(variableName, input1); + variableExists = jsEngine.HasVariable(variableName); + jsEngine.Execute(string.Format("{0} += '!';", variableName)); + output1 = jsEngine.GetVariableValue(variableName); + + jsEngine.SetVariableValue(variableName, input2); + output2 = jsEngine.GetVariableValue(variableName); + } + + // Assert + Assert.True(variableExists); + Assert.Equal(targetOutput1, output1); + + Assert.Equal(input2, output2); + } + + [Fact] + public virtual void SettingAndGettingVariableWithNameContainingUnicodeCharacters() + { + // Arrange + const string variableName = "слово"; + + const string input1 = "Hip-hip Hooray"; + const string targetOutput1 = "Hip-hip Hooray!"; + + const string input2 = "Hip-hip Hurrah"; + + // Act + bool variableExists; + string output1; + string output2; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.SetVariableValue(variableName, input1); + variableExists = jsEngine.HasVariable(variableName); + jsEngine.Execute(string.Format("{0} += '!';", variableName)); + output1 = jsEngine.GetVariableValue(variableName); + + jsEngine.SetVariableValue(variableName, input2); + output2 = jsEngine.GetVariableValue(variableName); + } + + // Assert + Assert.True(variableExists); + Assert.Equal(targetOutput1, output1); + + Assert.Equal(input2, output2); + } + + [Fact] + public virtual void RemovingVariable() { // Arrange const string variableName = "price"; @@ -762,8 +934,116 @@ public virtual void RemovingVariableIsCorrect() } // Assert - Assert.IsTrue(variableBeforeRemovingExists); - Assert.IsFalse(variableAfterRemovingExists); + Assert.True(variableBeforeRemovingExists); + Assert.False(variableAfterRemovingExists); + } + + [Fact] + public virtual void RemovingVariableWithNameContainingUnicodeCharacters() + { + // Arrange + const string variableName = "цена"; + const double input = 6780.00; + + // Act + bool variableBeforeRemovingExists; + bool variableAfterRemovingExists; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.SetVariableValue(variableName, input); + variableBeforeRemovingExists = jsEngine.HasVariable(variableName); + jsEngine.RemoveVariable(variableName); + variableAfterRemovingExists = jsEngine.HasVariable(variableName); + } + + // Assert + Assert.True(variableBeforeRemovingExists); + Assert.False(variableAfterRemovingExists); + } + + #endregion + + #region Script interruption + + [Fact] + public virtual void ScriptInterruption() + { + // Arrange + const string sleepyCode = @"function sleep(millisecondsTimeout) { + var totalMilliseconds = new Date().getTime() + millisecondsTimeout; + + while (new Date() < totalMilliseconds) + { } +} + +waitHandle.Set(); +sleep(5000);"; + + const string input = "!0"; + const bool targetOutput = true; + + // Act + bool supportsScriptInterruption; + Exception currentException = null; + bool output; + + using (var jsEngine = CreateJsEngine()) + { + supportsScriptInterruption = jsEngine.SupportsScriptInterruption; + if (supportsScriptInterruption) + { + using (var waitHandle = new ManualResetEvent(false)) + { + ThreadPool.QueueUserWorkItem(state => + { + waitHandle.WaitOne(); + jsEngine.Interrupt(); + }); + + jsEngine.EmbedHostObject("waitHandle", waitHandle); + + try + { + jsEngine.Execute(sleepyCode); + } + catch (Exception e) + { + currentException = e; + } + } + } + + output = jsEngine.Evaluate(input); + } + + // Assert + if (supportsScriptInterruption) + { + Assert.IsType(currentException); + } + Assert.Equal(targetOutput, output); + } + + #endregion + + #region Garbage collection + + [Fact] + public virtual void GarbageCollection() + { + // Arrange + const string input = @"arr = []; for (i = 0; i < 1000000; i++) { arr.push(arr); }"; + + // Act + using (var jsEngine = CreateJsEngine()) + { + if (jsEngine.SupportsGarbageCollection) + { + jsEngine.Execute(input); + jsEngine.CollectGarbage(); + } + } } #endregion diff --git a/test/JavaScriptEngineSwitcher.Tests/ErrorFormattingTests.cs b/test/JavaScriptEngineSwitcher.Tests/ErrorFormattingTests.cs new file mode 100644 index 00000000..bf4e242b --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/ErrorFormattingTests.cs @@ -0,0 +1,74 @@ +using Xunit; + +using JavaScriptEngineSwitcher.Core.Helpers; + +namespace JavaScriptEngineSwitcher.Tests +{ + public class ErrorFormattingTests + { + [Fact] + public void GettingSourceFragmentFromLine() + { + // Arrange + const string input1 = ""; + const string targetOutput1 = input1; + + const string input2 = " \n"; + const string targetOutput2 = input2; + + const string input3 = "var @variable3 = 678;"; + const string targetOutput3 = input3; + + const string input4 = " Math.hasOwnProperty(\"log2\")||(Math.log2=function(n){" + + "return Math.log(@n)*Math.LOG2E});"; + const string targetOutput4 = "…Math.hasOwnProperty(\"log2\")||(Math.log2=function(n){" + + "return Math.log(@n)*Math.LOG2E});"; + + const string input5 = "function mix(destination,source){var propertyName;destination=destination||{};" + + "for(propertyName in source){if(source.hasOwnProperty(propertyName){" + + "destination[propertyName]=source[propertyName]}}return destination}" + ; + const string targetOutput5 = "… in source){if(source.hasOwnProperty(propertyName){" + + "destination[propertyName]=source[propertyName]}}r…"; + + const string input6 = "Object.hasOwnProperty(\"assign)||(Object.assign=function(n){" + + "var u,i,f,t,r;if(typeof n==\"undefined\"||n===null)" + + "throw new TypeError(\"Object.assign: argument is not an Object.\");" + + "for(u=Object(n),f=arguments.length,i=1;i>18&0x3f;h2=bits>>12&0x3f;" + + "h3=bits>>6&0x3f;h4=bits&0x3f;enc+=b.charAt(h1)+b.charAt(h2)+b.charAt(h3)+b.charAt(h4)}" + + "while(i("output"); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Es5TestsBase.cs b/test/JavaScriptEngineSwitcher.Tests/Es5TestsBase.cs index 9336f3b4..bf1106be 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Es5TestsBase.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Es5TestsBase.cs @@ -1,19 +1,17 @@ -namespace JavaScriptEngineSwitcher.Tests -{ - using System; +using System; - using NUnit.Framework; +using Xunit; - using Core; +using JavaScriptEngineSwitcher.Core; - public abstract class Es5TestsBase +namespace JavaScriptEngineSwitcher.Tests +{ + public abstract class Es5TestsBase : TestsBase { - protected abstract IJsEngine CreateJsEngine(); - #region Array methods - [Test] - public virtual void ArrayEveryMethodIsSupported() + [Fact] + public virtual void SupportsArrayEveryMethod() { // Arrange const string initCode = "var engines = ['Chakra', 'V8', 'SpiderMonkey', 'Jurassic'];"; @@ -37,12 +35,12 @@ public virtual void ArrayEveryMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); } - [Test] - public virtual void ArrayFilterMethodIsSupported() + [Fact] + public virtual void SupportsArrayFilterMethod() { // Arrange const string initCode = "var engines = ['Chakra', 'V8', 'SpiderMonkey', 'Jurassic'];"; @@ -64,11 +62,11 @@ public virtual void ArrayFilterMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void ArrayForEachMethodIsSupported() + [Fact] + public virtual void SupportsArrayForEachMethod() { // Arrange const string resultVariableName = "enginesString"; @@ -94,11 +92,11 @@ public virtual void ArrayForEachMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void ArrayIndexOfMethodIsSupported() + [Fact] + public virtual void SupportsArrayIndexOfMethod() { // Arrange const string initCode = "var arr = [2, 5, 9, 2]"; @@ -147,17 +145,17 @@ public virtual void ArrayIndexOfMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); - Assert.AreEqual(targetOutput4, output4); - Assert.AreEqual(targetOutput5, output5); - Assert.AreEqual(targetOutput6, output6); - Assert.AreEqual(targetOutput7, output7); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); + Assert.Equal(targetOutput4, output4); + Assert.Equal(targetOutput5, output5); + Assert.Equal(targetOutput6, output6); + Assert.Equal(targetOutput7, output7); } - [Test] - public virtual void ArrayIsArrayMethodIsSupported() + [Fact] + public virtual void SupportsArrayIsArrayMethod() { // Arrange const string input1 = "Array.isArray({ length: 0 });"; @@ -177,12 +175,12 @@ public virtual void ArrayIsArrayMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); } - [Test] - public virtual void ArrayLastIndexOfMethodIsSupported() + [Fact] + public virtual void SupportsArrayLastIndexOfMethod() { // Arrange const string initCode = "var arr = [2, 5, 9, 2]"; @@ -231,17 +229,17 @@ public virtual void ArrayLastIndexOfMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); - Assert.AreEqual(targetOutput4, output4); - Assert.AreEqual(targetOutput5, output5); - Assert.AreEqual(targetOutput6, output6); - Assert.AreEqual(targetOutput7, output7); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); + Assert.Equal(targetOutput4, output4); + Assert.Equal(targetOutput5, output5); + Assert.Equal(targetOutput6, output6); + Assert.Equal(targetOutput7, output7); } - [Test] - public virtual void ArrayMapMethodIsSupported() + [Fact] + public virtual void SupportsArrayMapMethod() { // Arrange const string initCode = "var engines = ['Chakra', 'V8', 'SpiderMonkey', 'Jurassic'];"; @@ -263,11 +261,11 @@ public virtual void ArrayMapMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void ArrayReduceMethodIsSupported() + [Fact] + public virtual void SupportsArrayReduceMethod() { // Arrange const string input1 = @"[1, 2, 3, 4, 5].reduce(function (accum, value, index, array) { @@ -291,12 +289,12 @@ public virtual void ArrayReduceMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); } - [Test] - public virtual void ArrayReduceRightMethodIsSupported() + [Fact] + public virtual void SupportsArrayReduceRightMethod() { // Arrange const string input1 = @"[1, 2, 3, 4, 5].reduceRight(function (accum, value, index, array) { @@ -320,12 +318,12 @@ public virtual void ArrayReduceRightMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); } - [Test] - public virtual void ArraySomeMethodIsSupported() + [Fact] + public virtual void SupportsArraySomeMethod() { // Arrange const string initCode = "var engines = ['Chakra', 'V8', 'SpiderMonkey', 'Jurassic'];"; @@ -343,34 +341,35 @@ public virtual void ArraySomeMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } #endregion #region Date methods - [Test] - public virtual void DateNowMethodIsSupported() + [Fact] + public virtual void SupportsDateNowMethod() { // Arrange const string input = "Date.now();"; - DateTime targetOutput = DateTime.Now.ToUniversalTime(); + DateTime targetOutput; // Act DateTime output; using (var jsEngine = CreateJsEngine()) { + targetOutput = DateTime.Now.ToUniversalTime(); output = new DateTime(1970, 01, 01).AddMilliseconds(jsEngine.Evaluate(input)); } // Assert - Assert.IsTrue(Math.Abs((targetOutput - output).TotalMilliseconds) < 100); + Assert.True(Math.Abs((targetOutput - output).TotalMilliseconds) < 1000); } - [Test] - public virtual void DateToIsoStringMethodIsSupported() + [Fact] + public virtual void SupportsDateToIsoStringMethod() { // Arrange const string input = @"(new Date(1386696984000)).toISOString();"; @@ -385,29 +384,36 @@ public virtual void DateToIsoStringMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } #endregion #region Function methods - [Test] - public virtual void FunctionBindIsSupported() + [Fact] + public virtual void SupportsFunctionBindMethod() { // Arrange - const string initCode = @"var a = 5, - module = { - a: 12, - getA: function() { return this.a; } - }, - getA = module.getA + const string initCode = @"var A = (function () { + function A(a) { + this.a = a; + } + + A.prototype.getA = function() { + return this.a; + }; + + return A; + })(), + a = new A(5), + otherContext = { a: 12 } ;"; - const string input1 = "getA();"; + const string input1 = "a.getA();"; const int targetOutput1 = 5; - const string input2 = "getA.bind(module)();"; + const string input2 = "a.getA.bind(otherContext)();"; const int targetOutput2 = 12; // Act @@ -423,16 +429,16 @@ public virtual void FunctionBindIsSupported() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); } #endregion #region JSON methods - [Test] - public virtual void JsonParseMethodIsSupported() + [Fact] + public virtual void SupportsJsonParseMethod() { // Arrange const string initCode = "var obj = JSON.parse('{ \"foo\": \"bar\" }');"; @@ -449,11 +455,11 @@ public virtual void JsonParseMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void JsonStringifyMethodIsSupported() + [Fact] + public virtual void SupportsJsonStringifyMethod() { // Arrange const string initCode = @"var obj = new Object(); @@ -471,15 +477,15 @@ public virtual void JsonStringifyMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } #endregion #region Object methods - [Test] - public virtual void ObjectCreateMethodIsSupported() + [Fact] + public virtual void SupportsObjectCreateMethod() { // Arrange const string initCode1 = "var obj1 = Object.create(null);"; @@ -526,15 +532,15 @@ public virtual void ObjectCreateMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); - Assert.AreEqual(targetOutput3A, output3A); - Assert.AreEqual(targetOutput3B, output3B); + Assert.Equal(targetOutput3A, output3A); + Assert.Equal(targetOutput3B, output3B); } - [Test] - public virtual void ObjectKeysMethodIsSupported() + [Fact] + public virtual void SupportsObjectKeysMethod() { // Arrange const string input1 = "Object.keys(['a', 'b', 'c']).toString();"; @@ -570,18 +576,18 @@ public virtual void ObjectKeysMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); - Assert.AreEqual(targetOutput4, output4); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); + Assert.Equal(targetOutput4, output4); } #endregion #region String methods - [Test] - public virtual void StringSplitMethodIsCorrect() + [Fact] + public virtual void SupportsStringSplitMethod() { // Arrange const string input1 = "'aaaa'.split(/a/).length;"; @@ -606,13 +612,13 @@ public virtual void StringSplitMethodIsCorrect() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); } - [Test] - public virtual void StringTrimMethodIsSupported() + [Fact] + public virtual void SupportsStringTrimMethod() { // Arrange const string input = "' foo '.trim();"; @@ -627,7 +633,7 @@ public virtual void StringTrimMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } #endregion diff --git a/test/JavaScriptEngineSwitcher.Tests/EvalTestsBase.cs b/test/JavaScriptEngineSwitcher.Tests/EvalTestsBase.cs new file mode 100644 index 00000000..42f85428 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/EvalTestsBase.cs @@ -0,0 +1,47 @@ +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests +{ + public abstract class EvalTestsBase : TestsBase + { + [Fact] + public virtual void UsageOfEvalFunction() + { + // Arrange + const string input = "eval('2*2');"; + const int targetOutput = 4; + + // Act + int output; + + using (var jsEngine = CreateJsEngine()) + { + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void UsageOfFunctionConstructor() + { + // Arrange + const string input = "new Function('return 2*2;')();"; + const int targetOutput = 4; + + // Act + int output; + + using (var jsEngine = CreateJsEngine()) + { + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/FileSystemTestsBase.cs b/test/JavaScriptEngineSwitcher.Tests/FileSystemTestsBase.cs deleted file mode 100644 index de862d56..00000000 --- a/test/JavaScriptEngineSwitcher.Tests/FileSystemTestsBase.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace JavaScriptEngineSwitcher.Tests -{ - using System; - using System.IO; - using System.Text.RegularExpressions; - - public abstract class FileSystemTestsBase - { - /// - /// Regular expression for working with the `bin` directory path - /// - private readonly Regex _binDirRegex = new Regex(@"\\bin\\(?:Debug|Release)\\?$", RegexOptions.IgnoreCase); - - protected string _baseDirectoryPath; - - - protected FileSystemTestsBase() - { - string baseDirectoryPath = AppDomain.CurrentDomain.BaseDirectory; - if (_binDirRegex.IsMatch(baseDirectoryPath)) - { - baseDirectoryPath = Path.GetFullPath(Path.Combine(baseDirectoryPath, @"..\..\..\")); - } - - _baseDirectoryPath = baseDirectoryPath; - } - } -} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/declination-of-minutes.js b/test/JavaScriptEngineSwitcher.Tests/Files/declination-of-minutes.js new file mode 100644 index 00000000..41a3e4da --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/declination-of-minutes.js @@ -0,0 +1,23 @@ +function declensionOfNumerals(number, titles) { + var result, + 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 = titles[titleIndex]; + + return result; +} + +function declinationOfMinutes(number) { + return declensionOfNumerals(number, ['минута', 'минуты', 'минут']); +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/compilation-error/index.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/compilation-error/index.js new file mode 100644 index 00000000..353d86db --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/compilation-error/index.js @@ -0,0 +1,18 @@ +/*global evaluateFile */ +(function () { + 'use strict'; + + function calculateResult() { + var math = evaluateFile('./math'), + result = math.sum(math.cube(5), math.square(2), math.PI) + ; + + return result; + } + + var exports = { + calculateResult: calculateResult + }; + + return exports; +}()); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/compilation-error/math.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/compilation-error/math.js new file mode 100644 index 00000000..f64c9d4e --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/compilation-error/math.js @@ -0,0 +1,32 @@ +(function () { + 'use strict'; + + function sum() { + var result = 0, + i + ; + + for (i = 0; i < arguments.length; i++) { + result += arguments[i]; + } + + return result; + } + + function square(num) { + return num * num; + } + + function cube(num) { + return num * num * num; + } + + var exports = { + PI: 3,14, + sum: sum, + square: square, + cube: cube + }; + + return exports; +}()); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/host-error/index.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/host-error/index.js new file mode 100644 index 00000000..dafb42ef --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/host-error/index.js @@ -0,0 +1,18 @@ +/*global evaluateFile */ +(function () { + 'use strict'; + + function calculateResult() { + var math = evaluateFile('./match'), + result = math.sum(math.cube(5), math.square(2), math.PI) + ; + + return result; + } + + var exports = { + calculateResult: calculateResult + }; + + return exports; +}()); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/host-error/math.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/host-error/math.js new file mode 100644 index 00000000..829eb44d --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/host-error/math.js @@ -0,0 +1,32 @@ +(function () { + 'use strict'; + + function sum() { + var result = 0, + i + ; + + for (i = 0; i < arguments.length; i++) { + result += arguments[i]; + } + + return result; + } + + function square(num) { + return num * num; + } + + function cube(num) { + return num * num * num; + } + + var exports = { + PI: 3.14, + sum: sum, + square: square, + cube: cube + }; + + return exports; +}()); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/no-error/index.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/no-error/index.js new file mode 100644 index 00000000..353d86db --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/no-error/index.js @@ -0,0 +1,18 @@ +/*global evaluateFile */ +(function () { + 'use strict'; + + function calculateResult() { + var math = evaluateFile('./math'), + result = math.sum(math.cube(5), math.square(2), math.PI) + ; + + return result; + } + + var exports = { + calculateResult: calculateResult + }; + + return exports; +}()); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/no-error/math.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/no-error/math.js new file mode 100644 index 00000000..829eb44d --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/no-error/math.js @@ -0,0 +1,32 @@ +(function () { + 'use strict'; + + function sum() { + var result = 0, + i + ; + + for (i = 0; i < arguments.length; i++) { + result += arguments[i]; + } + + return result; + } + + function square(num) { + return num * num; + } + + function cube(num) { + return num * num * num; + } + + var exports = { + PI: 3.14, + sum: sum, + square: square, + cube: cube + }; + + return exports; +}()); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/runtime-error/index.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/runtime-error/index.js new file mode 100644 index 00000000..353d86db --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/runtime-error/index.js @@ -0,0 +1,18 @@ +/*global evaluateFile */ +(function () { + 'use strict'; + + function calculateResult() { + var math = evaluateFile('./math'), + result = math.sum(math.cube(5), math.square(2), math.PI) + ; + + return result; + } + + var exports = { + calculateResult: calculateResult + }; + + return exports; +}()); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/runtime-error/math.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/runtime-error/math.js new file mode 100644 index 00000000..7bc5a5ab --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-evaluation/runtime-error/math.js @@ -0,0 +1,32 @@ +(function () { + 'use strict'; + + function sum() { + var result = 0, + i + ; + + for (i = 0; i < arguments.length; i++) { + result += argumens[i]; + } + + return result; + } + + function square(num) { + return num * num; + } + + function cube(num) { + return num * num * num; + } + + var exports = { + PI: 3.14, + sum: sum, + square: square, + cube: cube + }; + + return exports; +}()); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/compilation-error/first-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/compilation-error/first-file.js new file mode 100644 index 00000000..c2d9f751 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/compilation-error/first-file.js @@ -0,0 +1,2 @@ +num = num * 3; +executeFile(directoryPath + "/second-file.js"); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/compilation-error/main-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/compilation-error/main-file.js new file mode 100644 index 00000000..0d337c54 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/compilation-error/main-file.js @@ -0,0 +1,2 @@ +var num = 5; +executeFile(directoryPath + "/first-file.js"); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/compilation-error/second-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/compilation-error/second-file.js new file mode 100644 index 00000000..0c2473f0 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/compilation-error/second-file.js @@ -0,0 +1 @@ +num -# 3; \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/host-error/first-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/host-error/first-file.js new file mode 100644 index 00000000..353165af --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/host-error/first-file.js @@ -0,0 +1,2 @@ +num = num * 3; +executeFile(directoryPath + "/second-file.jsx"); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/host-error/main-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/host-error/main-file.js new file mode 100644 index 00000000..0d337c54 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/host-error/main-file.js @@ -0,0 +1,2 @@ +var num = 5; +executeFile(directoryPath + "/first-file.js"); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/host-error/second-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/host-error/second-file.js new file mode 100644 index 00000000..8588ae03 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/host-error/second-file.js @@ -0,0 +1 @@ +num -= 3; \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/no-error/first-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/no-error/first-file.js new file mode 100644 index 00000000..c2d9f751 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/no-error/first-file.js @@ -0,0 +1,2 @@ +num = num * 3; +executeFile(directoryPath + "/second-file.js"); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/no-error/main-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/no-error/main-file.js new file mode 100644 index 00000000..0d337c54 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/no-error/main-file.js @@ -0,0 +1,2 @@ +var num = 5; +executeFile(directoryPath + "/first-file.js"); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/no-error/second-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/no-error/second-file.js new file mode 100644 index 00000000..8588ae03 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/no-error/second-file.js @@ -0,0 +1 @@ +num -= 3; \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/runtime-error/first-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/runtime-error/first-file.js new file mode 100644 index 00000000..c2d9f751 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/runtime-error/first-file.js @@ -0,0 +1,2 @@ +num = num * 3; +executeFile(directoryPath + "/second-file.js"); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/runtime-error/main-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/runtime-error/main-file.js new file mode 100644 index 00000000..0d337c54 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/runtime-error/main-file.js @@ -0,0 +1,2 @@ +var num = 5; +executeFile(directoryPath + "/first-file.js"); \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/runtime-error/second-file.js b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/runtime-error/second-file.js new file mode 100644 index 00000000..32ba02c0 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Files/recursive-execution/runtime-error/second-file.js @@ -0,0 +1 @@ +nuм -= 3; \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/AnimalTrainer.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/AnimalTrainer.cs new file mode 100644 index 00000000..deccb6c1 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/AnimalTrainer.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Interop.Animals +{ + public sealed class AnimalTrainer + { + public string ExecuteVoiceCommand(IAnimal animal) + { + return animal.Cry(); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/Cat.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/Cat.cs new file mode 100644 index 00000000..ff0681ae --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/Cat.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Interop.Animals +{ + public sealed class Cat : IAnimal + { + public string Cry() + { + return "Meow!"; + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/Dog.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/Dog.cs new file mode 100644 index 00000000..4593c686 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/Dog.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Interop.Animals +{ + public sealed class Dog : IAnimal + { + public string Cry() + { + return "Woof!"; + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/IAnimal.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/IAnimal.cs new file mode 100644 index 00000000..823747d1 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Animals/IAnimal.cs @@ -0,0 +1,7 @@ +namespace JavaScriptEngineSwitcher.Tests.Interop.Animals +{ + public interface IAnimal + { + string Cry(); + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Base64Encoder.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Base64Encoder.cs index 3faf7d23..884be91b 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Interop/Base64Encoder.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Base64Encoder.cs @@ -1,8 +1,8 @@ -namespace JavaScriptEngineSwitcher.Tests.Interop -{ - using System; - using System.Text; +using System; +using System.Text; +namespace JavaScriptEngineSwitcher.Tests.Interop +{ public static class Base64Encoder { public const int DATA_URI_MAX = 32768; @@ -10,7 +10,7 @@ public static class Base64Encoder public static string Encode(string value) { - return Convert.ToBase64String(Encoding.Default.GetBytes(value)); + return Convert.ToBase64String(Encoding.GetEncoding(0).GetBytes(value)); } } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/BundleTable.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/BundleTable.cs index 36673361..b9e477dd 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Interop/BundleTable.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/BundleTable.cs @@ -4,6 +4,8 @@ public static class BundleTable { private static bool _enableOptimizations = true; + public static object SyncRoot = new object(); + public static bool EnableOptimizations { get diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Date.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Date.cs index 21910b21..ae1cda1b 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Interop/Date.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Date.cs @@ -1,7 +1,7 @@ -namespace JavaScriptEngineSwitcher.Tests.Interop -{ - using System; +using System; +namespace JavaScriptEngineSwitcher.Tests.Interop +{ public struct Date { private static readonly int[] _cumulativeDays = { 0, 31, 59, 90, 120, 151, 181, @@ -16,7 +16,7 @@ public static Date Today get { DateTime currentDateTime = DateTime.Today; - Date currentDate = new Date(currentDateTime.Year, currentDateTime.Month, currentDateTime.Day); + var currentDate = new Date(currentDateTime.Year, currentDateTime.Month, currentDateTime.Day); return currentDate; } @@ -43,5 +43,13 @@ public int GetDayOfYear() (Month > 2 && IsLeapYear(Year) ? 1 : 0) ; } + + public Date AddDays(double value) + { + var dateTime = new DateTime(Year, Month, Day); + DateTime newDateTime = dateTime.AddDays(value); + + return new Date(newDateTime.Year, newDateTime.Month, newDateTime.Day); + } } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/FileManager.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/FileManager.cs index 246662ca..6b288e72 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Interop/FileManager.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/FileManager.cs @@ -1,18 +1,26 @@ -namespace JavaScriptEngineSwitcher.Tests.Interop -{ - using System; - using System.IO; +using System; +using System.IO; +using System.Text; +namespace JavaScriptEngineSwitcher.Tests.Interop +{ public sealed class FileManager { public string ReadFile(string path) + { + return ReadFile(path, null); + } + + public string ReadFile(string path, Encoding encoding) { if (path == null) { throw new ArgumentNullException("path"); } - string content = File.ReadAllText(path); + encoding = encoding ?? Encoding.UTF8; + + string content = File.ReadAllText(path, encoding); return content; } diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/DefaultLogger.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/DefaultLogger.cs new file mode 100644 index 00000000..b4c8fdeb --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/DefaultLogger.cs @@ -0,0 +1,8 @@ +namespace JavaScriptEngineSwitcher.Tests.Interop.Logging +{ + public class DefaultLogger + { + public static object SyncRoot = new object(); + public static ILogger Current = new NullLogger(); + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/ILogger.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/ILogger.cs new file mode 100644 index 00000000..4daa5fc2 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/ILogger.cs @@ -0,0 +1,17 @@ +namespace JavaScriptEngineSwitcher.Tests.Interop.Logging +{ + public interface ILogger + { + void Error(string category, string message, + string filePath = "", int lineNumber = 0, int columnNumber = 0, + string sourceFragment = ""); + + void Warn(string category, string message, + string filePath = "", int lineNumber = 0, int columnNumber = 0, + string sourceFragment = ""); + + void Debug(string category, string message, string filePath = ""); + + void Info(string category, string message, string filePath = ""); + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/NullLogger.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/NullLogger.cs new file mode 100644 index 00000000..9ed97b1b --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/NullLogger.cs @@ -0,0 +1,26 @@ +namespace JavaScriptEngineSwitcher.Tests.Interop.Logging +{ + public sealed class NullLogger : ILogger + { + public void Error(string category, string message, + string filePath = "", int lineNumber = 0, int columnNumber = 0, + string sourceFragment = "") + { } + + public void Warn(string category, string message, + string filePath = "", int lineNumber = 0, int columnNumber = 0, + string sourceFragment = "") + { } + + public void Debug(string category, string message, string filePath = "") + { } + + public void Info(string category, string message, string filePath = "") + { } + + public override string ToString() + { + return "[null logger]"; + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/ThrowExceptionLogger.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/ThrowExceptionLogger.cs new file mode 100644 index 00000000..b8bf810d --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Logging/ThrowExceptionLogger.cs @@ -0,0 +1,57 @@ +using System; +using System.Text; + +namespace JavaScriptEngineSwitcher.Tests.Interop.Logging +{ + public sealed class ThrowExceptionLogger : ILogger + { + public void Error(string category, string message, string filePath = "", + int lineNumber = 0, int columnNumber = 0, string sourceFragment = "") + { + var errorBuilder = new StringBuilder(); + errorBuilder.AppendLine("Category: " + category); + errorBuilder.AppendLine("Message: " + message); + + if (!string.IsNullOrWhiteSpace(filePath)) + { + errorBuilder.AppendLine("File: " + filePath); + } + + if (lineNumber > 0) + { + errorBuilder.AppendLine("Line number: " + lineNumber); + } + + if (columnNumber > 0) + { + errorBuilder.AppendLine("Column number: " + columnNumber); + } + + if (!string.IsNullOrWhiteSpace(sourceFragment)) + { + errorBuilder.AppendLine("Source fragment:" + Environment.NewLine + sourceFragment); + } + + string errorMessage = errorBuilder.ToString(); + errorBuilder.Clear(); + + throw new InvalidOperationException(errorMessage); + } + + public void Warn(string category, string message, + string filePath = "", int lineNumber = 0, int columnNumber = 0, + string sourceFragment = "") + { } + + public void Debug(string category, string message, string filePath = "") + { } + + public void Info(string category, string message, string filePath = "") + { } + + public override string ToString() + { + return "[throw exception logger]"; + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/LoginFailedException.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/LoginFailedException.cs new file mode 100644 index 00000000..a55ff7ba --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/LoginFailedException.cs @@ -0,0 +1,56 @@ +using System; +using System.Runtime.Serialization; + +namespace JavaScriptEngineSwitcher.Tests.Interop +{ + [Serializable] + public class LoginFailedException : Exception + { + private string _userName; + + public string UserName + { + get { return _userName; } + set { _userName = value; } + } + + + public LoginFailedException() + { } + + public LoginFailedException(string message) + : base(message) + { } + + public LoginFailedException(string message, Exception innerException) + : base(message, innerException) + { } + +#if NET8_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif + protected LoginFailedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info != null) + { + _userName = info.GetString("UserName"); + } + } + + +#if NET8_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + base.GetObjectData(info, context); + info.AddValue("UserName", this._userName); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Person.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Person.cs index 81f2502b..86f7fd46 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Interop/Person.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Person.cs @@ -14,15 +14,26 @@ public string LastName set; } + public string Patronymic + { + get; + set; + } + public Person() : this(string.Empty, string.Empty) { } public Person(string firstName, string lastName) + : this(firstName, lastName, string.Empty) + { } + + public Person(string firstName, string lastName, string patronymic) { FirstName = firstName; LastName = lastName; + Patronymic = patronymic; } diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Product.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Product.cs index ae122a2c..bc0f4a24 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Interop/Product.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Product.cs @@ -3,6 +3,7 @@ public sealed class Product { public string Name; + public string Description; public double Price; } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/SimpleSingleton.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/SimpleSingleton.cs deleted file mode 100644 index 510b7d72..00000000 --- a/test/JavaScriptEngineSwitcher.Tests/Interop/SimpleSingleton.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace JavaScriptEngineSwitcher.Tests.Interop -{ - public class SimpleSingleton - { - public static readonly SimpleSingleton Instance = new SimpleSingleton(); - - - private SimpleSingleton() - { } - - - public override string ToString() - { - return "[simple singleton]"; - } - } -} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Interop/Temperature.cs b/test/JavaScriptEngineSwitcher.Tests/Interop/Temperature.cs index 3a54d509..35c8929c 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Interop/Temperature.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Interop/Temperature.cs @@ -1,7 +1,7 @@ -namespace JavaScriptEngineSwitcher.Tests.Interop -{ - using System; +using System; +namespace JavaScriptEngineSwitcher.Tests.Interop +{ public struct Temperature { double _celsius; diff --git a/test/JavaScriptEngineSwitcher.Tests/InteropTestsBase.cs b/test/JavaScriptEngineSwitcher.Tests/InteropTestsBase.cs index 943f5799..978533a6 100644 --- a/test/JavaScriptEngineSwitcher.Tests/InteropTestsBase.cs +++ b/test/JavaScriptEngineSwitcher.Tests/InteropTestsBase.cs @@ -1,27 +1,29 @@ -namespace JavaScriptEngineSwitcher.Tests -{ - using System; - using System.Collections.Generic; - using System.Drawing; - using System.IO; - using System.Linq; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; - using NUnit.Framework; +using Xunit; - using Core; - using Interop; +using JavaScriptEngineSwitcher.Core; - [TestFixture] - public abstract class InteropTestsBase : FileSystemTestsBase - { - protected abstract IJsEngine CreateJsEngine(); +using JavaScriptEngineSwitcher.Tests.Interop; +using JavaScriptEngineSwitcher.Tests.Interop.Animals; +using JavaScriptEngineSwitcher.Tests.Interop.Logging; +namespace JavaScriptEngineSwitcher.Tests +{ + public abstract class InteropTestsBase : TestsBase + { #region Embedding of objects #region Objects with fields - [Test] - public virtual void EmbeddingOfInstanceOfCustomValueTypeWithFieldsIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfCustomValueTypeWithFields() { // Arrange var date = new Date(2015, 12, 29); @@ -52,22 +54,24 @@ public virtual void EmbeddingOfInstanceOfCustomValueTypeWithFieldsIsCorrect() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); } - [Test] - public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithFieldsIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithFields() { // Arrange var product = new Product { Name = "Red T-shirt", + Description = string.Empty, Price = 995.00 }; - const string updateCode = "product.Price *= 1.15;"; + const string updateCode = @"product.Description = null; +product.Price *= 1.15;"; const string input1 = "product.Name"; const string targetOutput1 = "Red T-shirt"; @@ -89,16 +93,16 @@ public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithFieldsIsCorrect( } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); } #endregion #region Objects with properties - [Test] - public virtual void EmbeddingOfInstanceOfBuiltinValueTypeWithPropertiesIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfBuiltinValueTypeWithProperties() { // Arrange var timeSpan = new TimeSpan(4840780000000); @@ -132,14 +136,14 @@ public virtual void EmbeddingOfInstanceOfBuiltinValueTypeWithPropertiesIsCorrect } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); - Assert.AreEqual(targetOutput4, output4); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); + Assert.Equal(targetOutput4, output4); } - [Test] - public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithPropertiesIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithProperties() { // Arrange var uri = new Uri("https://github.com/Taritsyn/MsieJavaScriptEngine"); @@ -168,13 +172,13 @@ public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithPropertiesIsCor } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); } - [Test] - public virtual void EmbeddingOfInstanceOfCustomValueTypeWithPropertiesIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfCustomValueTypeWithProperties() { // Arrange var temperature = new Temperature(-17.3, TemperatureUnits.Celsius); @@ -203,17 +207,18 @@ public virtual void EmbeddingOfInstanceOfCustomValueTypeWithPropertiesIsCorrect( } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); } - [Test] - public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithPropertiesIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithProperties() { // Arrange var person = new Person("Vanya", "Ivanov"); - const string updateCode = "person.LastName = person.LastName.substr(0, 5) + 'ff';"; + const string updateCode = @"person.LastName = person.LastName.substr(0, 5) + 'ff'; +person.Patronymic = null;"; const string input1 = "person.FirstName"; const string targetOutput1 = "Vanya"; @@ -235,28 +240,92 @@ public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithPropertiesIsCorr } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + } + + [Fact] + public virtual void EmbeddingOfInstanceOfAnonymousTypeWithProperties() + { + // Arrange + var person = new + { + FirstName = "John", + LastName = "Doe", + Address = new + { + StreetAddress = "103 Elm Street", + City = "Atlanta", + State = "GA", + PostalCode = 30339 + } + }; + + const string input1 = "person.FirstName"; + const string targetOutput1 = "John"; + + const string input2 = "person.LastName"; + const string targetOutput2 = "Doe"; + + const string input3 = "person.Address.StreetAddress"; + const string targetOutput3 = "103 Elm Street"; + + const string input4 = "person.Address.City"; + const string targetOutput4 = "Atlanta"; + + const string input5 = "person.Address.State"; + const string targetOutput5 = "GA"; + + const string input6 = "person.Address.PostalCode"; + const int targetOutput6 = 30339; + + // Act + string output1; + string output2; + string output3; + string output4; + string output5; + int output6; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("person", person); + + output1 = jsEngine.Evaluate(input1); + output2 = jsEngine.Evaluate(input2); + output3 = jsEngine.Evaluate(input3); + output4 = jsEngine.Evaluate(input4); + output5 = jsEngine.Evaluate(input5); + output6 = jsEngine.Evaluate(input6); + } + + // Assert + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); + Assert.Equal(targetOutput4, output4); + Assert.Equal(targetOutput5, output5); + Assert.Equal(targetOutput6, output6); } #endregion #region Objects with methods - [Test] - public virtual void EmbeddingOfInstanceOfBuiltinValueTypeWithMethodsIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfBuiltinValueTypeWithMethods() { // Arrange var color = Color.FromArgb(84, 139, 212); const string input1 = "color.GetHue()"; - const double targetOutput1 = 214.21875d; + const double targetOutput1 = 214.21875; const string input2 = "color.GetSaturation()"; - const double targetOutput2 = 0.59813079999999996d; + const double targetOutput2 = 0.59813; const string input3 = "color.GetBrightness()"; - const double targetOutput3 = 0.58039220000000002d; + const double targetOutput3 = 0.58039; // Act double output1; @@ -267,19 +336,19 @@ public virtual void EmbeddingOfInstanceOfBuiltinValueTypeWithMethodsIsCorrect() { jsEngine.EmbedHostObject("color", color); - output1 = Math.Round(jsEngine.Evaluate(input1), 7); - output2 = Math.Round(jsEngine.Evaluate(input2), 7); - output3 = Math.Round(jsEngine.Evaluate(input3), 7); + output1 = Math.Round(jsEngine.Evaluate(input1), 5); + output2 = Math.Round(jsEngine.Evaluate(input2), 5); + output3 = Math.Round(jsEngine.Evaluate(input3), 5); } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); } - [Test] - public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithMethodIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithMethod() { // Arrange var random = new Random(); @@ -297,39 +366,46 @@ public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithMethodIsCorrect } // Assert - Assert.IsTrue(targetOutput.Contains(output)); + Assert.Contains(output, targetOutput); } - [Test] - public virtual void EmbeddingOfInstanceOfCustomValueTypeWithMethodIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfCustomValueTypeWithMethods() { // Arrange var programmerDayDate = new Date(2015, 9, 13); - const string input = "programmerDay.GetDayOfYear()"; - const int targetOutput = 256; + const string input1 = "programmerDay.GetDayOfYear()"; + const int targetOutput1 = 256; + + const string input2 = @"var smileDay = programmerDay.AddDays(6); +smileDay.GetDayOfYear();"; + const int targetOutput2 = 262; // Act - int output; + int output1; + int output2; using (var jsEngine = CreateJsEngine()) { jsEngine.EmbedHostObject("programmerDay", programmerDayDate); - output = jsEngine.Evaluate(input); + output1 = jsEngine.Evaluate(input1); + output2 = jsEngine.Evaluate(input2); } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); } - [Test] - public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithMethodIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithMethod() { // Arrange var fileManager = new FileManager(); - string filePath = Path.GetFullPath(Path.Combine(_baseDirectoryPath, "JavaScriptEngineSwitcher.Tests/Files/link.txt")); + const string filePath = "Files/link.txt"; - string input = string.Format("fileManager.ReadFile('{0}')", filePath.Replace(@"\", @"\\")); + string input = string.Format("fileManager.ReadFile('{0}', null)", filePath.Replace(@"\", @"\\")); const string targetOutput = "http://www.panopticoncentral.net/2015/09/09/the-two-faces-of-jsrt-in-windows-10/"; // Act @@ -342,15 +418,114 @@ public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithMethodIsCorrect( } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void EmbeddingOfInstancesOfCustomReferenceTypesAndCallingOfMethodOfWithInterfaceParameter() + { + // Arrange + var animalTrainer = new AnimalTrainer(); + var cat = new Cat(); + var dog = new Dog(); + + const string input1 = "animalTrainer.ExecuteVoiceCommand(cat)"; + const string targetOutput1 = "Meow!"; + + const string input2 = "animalTrainer.ExecuteVoiceCommand(dog)"; + const string targetOutput2 = "Woof!"; + + // Act + string output1; + string output2; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("animalTrainer", animalTrainer); + jsEngine.EmbedHostObject("cat", cat); + jsEngine.EmbedHostObject("dog", dog); + output1 = jsEngine.Evaluate(input1); + output2 = jsEngine.Evaluate(input2); + } + + // Assert + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + } + + [Fact] + public virtual void EmbeddingOfInstanceOfCustomValueTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + var date = new Date(); + + const string input = "date.GetType();"; + string targetOutput = typeof(Date).FullName; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("date", date); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void EmbeddingOfInstanceOfCustomReferenceTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + var cat = new Cat(); + + const string input = "cat.GetType();"; + string targetOutput = typeof(Cat).FullName; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("cat", cat); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void EmbeddingOfInstanceOfAssemblyTypeAndCallingOfItsCreateInstanceMethod() + { + // Arrange + Assembly assembly = this.GetType().Assembly; + string personTypeName = typeof(Person).FullName; + + string input = string.Format("assembly.CreateInstance(\"{0}\");", personTypeName); + const string targetOutput = "{FirstName=,LastName=}"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("assembly", assembly); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); } #endregion #region Delegates - [Test] - public virtual void EmbeddingOfInstanceOfDelegateWithoutParametersIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfDelegateWithoutParameters() { // Arrange var generateRandomStringFunc = new Func(() => @@ -384,12 +559,12 @@ public virtual void EmbeddingOfInstanceOfDelegateWithoutParametersIsCorrect() } // Assert - Assert.IsNotNullOrEmpty(output); - Assert.IsTrue(output.Length == targetOutputLength); + Assert.NotEmpty(output); + Assert.True(output.Length == targetOutputLength); } - [Test] - public virtual void EmbeddingOfInstanceOfDelegateWithOneParameterIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfDelegateWithOneParameter() { // Arrange var squareFunc = new Func(a => a * a); @@ -407,11 +582,11 @@ public virtual void EmbeddingOfInstanceOfDelegateWithOneParameterIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EmbeddingOfInstanceOfDelegateWithTwoParametersIsCorrect() + [Fact] + public virtual void EmbeddingOfInstanceOfDelegateWithTwoParameters() { // Arrange var sumFunc = new Func((a, b) => a + b); @@ -429,7 +604,237 @@ public virtual void EmbeddingOfInstanceOfDelegateWithTwoParametersIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void EmbeddingOfInstanceOfDelegateWithoutResult() + { + // Arrange + var logBuilder = new StringBuilder(); + Action log = (string value) => + { + logBuilder.AppendLine(value); + }; + + const string input = @"(function(log, undefined) { + var num = 2, count = 0; + + log('-= Start code execution =-'); + + while (num != Infinity) { + num = num * num; + count++; + } + + log('-= End of code execution =-'); + + return count; +}(log));"; + const int targetOutput = 10; + string targetLogOutput = "-= Start code execution =-" + Environment.NewLine + + "-= End of code execution =-" + Environment.NewLine; + + // Act + int output; + string logOutput; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("log", log); + output = jsEngine.Evaluate(input); + + logOutput = logBuilder.ToString(); + logBuilder.Clear(); + } + + // Assert + Assert.Equal(targetOutput, output); + Assert.Equal(targetLogOutput, logOutput); + } + + [Fact] + public virtual void EmbeddingOfInstanceOfDelegateAndCheckingItsPrototype() + { + // Arrange + var someFunc = new Func(() => 42); + + const string input = "Object.getPrototypeOf(embeddedFunc) === Function.prototype"; + + // Act + bool output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("embeddedFunc", someFunc); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.True(output); + } + + [Fact] + public virtual void EmbeddingOfInstanceOfDelegateAndCallingItWithMissingParameter() + { + // Arrange + var sumFunc = new Func((a, b) => a + b); + + const string input = "sum(678)"; + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("sum", sumFunc); + + try + { + int result = jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + } + + [Fact] + public virtual void EmbeddingOfInstanceOfDelegateAndCallingItWithExtraParameter() + { + // Arrange + var sumFunc = new Func((a, b) => a + b); + + const string input = "sum(678, 711, 611)"; + const int targetOutput = 1389; + + // Act + int output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("sum", sumFunc); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void EmbeddingOfInstanceOfDelegateAndGettingItsMethodProperty() + { + // Arrange + var cat = new Cat(); + var cryFunc = new Func(cat.Cry); + + const string input = "cry.Method;"; + string targetOutput = "undefined"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("cry", cryFunc); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + #endregion + + #region Recursive calls + + [Fact] + public virtual void RecursiveEvaluationOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-evaluation/no-error"; + const string input = "evaluateFile('index').calculateResult();"; + const double targetOutput = 132.14; + + // Act + double output; + + using (var jsEngine = CreateJsEngine()) + { + Func evaluateFile = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("evaluateFile", evaluateFile); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void RecursiveExecutionOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-execution/no-error"; + const string variableName = "num"; + const int targetOutput = 12; + + // Act + int output; + + using (var jsEngine = CreateJsEngine()) + { + Action executeFile = path => jsEngine.ExecuteFile(path); + + jsEngine.SetVariableValue("directoryPath", directoryPath); + jsEngine.EmbedHostObject("executeFile", executeFile); + jsEngine.ExecuteFile(Path.Combine(directoryPath, "main-file.js")); + + output = jsEngine.GetVariableValue(variableName); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + #endregion + + #region Removal + + [Fact] + public virtual void RemovingOfEmbeddedInstanceOfCustomReferenceType() + { + // Arrange + var person = new Person("Vasya", "Pupkin"); + + // Act + Exception currentException = null; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("person", person); + + try + { + jsEngine.RemoveVariable("person"); + } + catch (Exception e) + { + currentException = e; + } + } + + // Assert + Assert.Null(currentException); } #endregion @@ -441,8 +846,8 @@ public virtual void EmbeddingOfInstanceOfDelegateWithTwoParametersIsCorrect() #region Creating of instances - [Test] - public virtual void CreatingAnInstanceOfEmbeddedBuiltinValueTypeIsCorrect() + [Fact] + public virtual void CreatingAnInstanceOfEmbeddedBuiltinValueType() { // Arrange Type pointType = typeof(Point); @@ -460,11 +865,11 @@ public virtual void CreatingAnInstanceOfEmbeddedBuiltinValueTypeIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void CreatingAnInstanceOfEmbeddedBuiltinReferenceTypeIsCorrect() + [Fact] + public virtual void CreatingAnInstanceOfEmbeddedBuiltinReferenceType() { // Arrange Type uriType = typeof(Uri); @@ -486,11 +891,11 @@ public virtual void CreatingAnInstanceOfEmbeddedBuiltinReferenceTypeIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void CreatingAnInstanceOfEmbeddedCustomValueTypeIsCorrect() + [Fact] + public virtual void CreatingAnInstanceOfEmbeddedCustomValueType() { // Arrange Type point3DType = typeof(Point3D); @@ -508,11 +913,11 @@ public virtual void CreatingAnInstanceOfEmbeddedCustomValueTypeIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void CreatingAnInstanceOfEmbeddedCustomReferenceTypeIsCorrect() + [Fact] + public virtual void CreatingAnInstanceOfEmbeddedCustomReferenceType() { // Arrange Type personType = typeof(Person); @@ -530,15 +935,58 @@ public virtual void CreatingAnInstanceOfEmbeddedCustomReferenceTypeIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void CreatingAnInstanceOfEmbeddedBuiltinExceptionAndGettingItsTargetSiteProperty() + { + // Arrange + Type invalidOperationExceptionType = typeof(InvalidOperationException); + + const string input = "new InvalidOperationError(\"A terrible thing happened!\").TargetSite;"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("InvalidOperationError", invalidOperationExceptionType); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Null(output); + } + + [Fact] + public virtual void CreatingAnInstanceOfEmbeddedCustomExceptionAndCallingOfItsGetTypeMethod() + { + // Arrange + Type loginFailedExceptionType = typeof(LoginFailedException); + + const string input = "new LoginFailedError(\"Wrong password entered!\").GetType();"; + string targetOutput = loginFailedExceptionType.FullName; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("LoginFailedError", loginFailedExceptionType); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); } #endregion #region Types with constants - [Test] - public virtual void EmbeddingOfBuiltinReferenceTypeWithConstantsIsCorrect() + [Fact] + public virtual void EmbeddingOfBuiltinReferenceTypeWithConstants() { // Arrange Type mathType = typeof(Math); @@ -562,12 +1010,12 @@ public virtual void EmbeddingOfBuiltinReferenceTypeWithConstantsIsCorrect() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); } - [Test] - public virtual void EmbeddingOfCustomValueTypeWithConstantsIsCorrect() + [Fact] + public virtual void EmbeddingOfCustomValueTypeWithConstants() { // Arrange Type predefinedStringsType = typeof(PredefinedStrings); @@ -596,13 +1044,13 @@ public virtual void EmbeddingOfCustomValueTypeWithConstantsIsCorrect() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); } - [Test] - public virtual void EmbeddingOfCustomReferenceTypeWithConstantIsCorrect() + [Fact] + public virtual void EmbeddingOfCustomReferenceTypeWithConstant() { // Arrange Type base64EncoderType = typeof(Base64Encoder); @@ -620,15 +1068,15 @@ public virtual void EmbeddingOfCustomReferenceTypeWithConstantIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } #endregion #region Types with fields - [Test] - public virtual void EmbeddingOfBuiltinValueTypeWithFieldIsCorrect() + [Fact] + public virtual void EmbeddingOfBuiltinValueTypeWithField() { // Arrange Type guidType = typeof(Guid); @@ -646,11 +1094,11 @@ public virtual void EmbeddingOfBuiltinValueTypeWithFieldIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EmbeddingOfBuiltinReferenceTypeWithFieldIsCorrect() + [Fact] + public virtual void EmbeddingOfBuiltinReferenceTypeWithField() { // Arrange Type bitConverterType = typeof(BitConverter); @@ -668,11 +1116,11 @@ public virtual void EmbeddingOfBuiltinReferenceTypeWithFieldIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EmbeddingOfCustomValueTypeWithFieldIsCorrect() + [Fact] + public virtual void EmbeddingOfCustomValueTypeWithField() { // Arrange Type point3DType = typeof(Point3D); @@ -690,37 +1138,48 @@ public virtual void EmbeddingOfCustomValueTypeWithFieldIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EmbeddingOfCustomReferenceTypeWithFieldIsCorrect() + [Fact] + public virtual void EmbeddingOfCustomReferenceTypeWithField() { // Arrange - Type simpleSingletonType = typeof(SimpleSingleton); + Type defaultLoggerType = typeof(DefaultLogger); + Type throwExceptionLoggerType = typeof(ThrowExceptionLogger); + const string updateCode = @"var oldLogger = DefaultLogger.Current; +DefaultLogger.Current = new ThrowExceptionLogger();"; + const string rollbackCode = "DefaultLogger.Current = oldLogger;"; - const string input = "SimpleSingleton.Instance.ToString()"; - const string targetOutput = "[simple singleton]"; + const string input = "DefaultLogger.Current.ToString()"; + const string targetOutput = "[throw exception logger]"; // Act string output; using (var jsEngine = CreateJsEngine()) { - jsEngine.EmbedHostType("SimpleSingleton", simpleSingletonType); - output = jsEngine.Evaluate(input); + jsEngine.EmbedHostType("DefaultLogger", defaultLoggerType); + jsEngine.EmbedHostType("ThrowExceptionLogger", throwExceptionLoggerType); + + lock (DefaultLogger.SyncRoot) + { + jsEngine.Execute(updateCode); + output = jsEngine.Evaluate(input); + jsEngine.Execute(rollbackCode); + } } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } #endregion #region Types with properties - [Test] - public virtual void EmbeddingOfBuiltinValueTypeWithPropertyIsCorrect() + [Fact] + public virtual void EmbeddingOfBuiltinValueTypeWithProperty() { // Arrange Type colorType = typeof(Color); @@ -738,11 +1197,11 @@ public virtual void EmbeddingOfBuiltinValueTypeWithPropertyIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EmbeddingOfBuiltinReferenceTypeWithPropertyIsCorrect() + [Fact] + public virtual void EmbeddingOfBuiltinReferenceTypeWithProperty() { // Arrange Type environmentType = typeof(Environment); @@ -760,11 +1219,11 @@ public virtual void EmbeddingOfBuiltinReferenceTypeWithPropertyIsCorrect() } // Assert - Assert.IsTrue(targetOutput.Contains(output)); + Assert.Contains(output, targetOutput); } - [Test] - public virtual void EmbeddingOfCustomValueTypeWithPropertyIsCorrect() + [Fact] + public virtual void EmbeddingOfCustomValueTypeWithProperty() { // Arrange Type dateType = typeof(Date); @@ -793,15 +1252,17 @@ public virtual void EmbeddingOfCustomValueTypeWithPropertyIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EmbeddingOfCustomReferenceTypeWithPropertyIsCorrect() + [Fact] + public virtual void EmbeddingOfCustomReferenceTypeWithProperty() { // Arrange Type bundleTableType = typeof(BundleTable); - const string updateCode = "BundleTable.EnableOptimizations = false;"; + const string updateCode = @"var oldEnableOptimizationsValue = BundleTable.EnableOptimizations; +BundleTable.EnableOptimizations = false;"; + const string rollbackCode = "BundleTable.EnableOptimizations = oldEnableOptimizationsValue;"; const string input = "BundleTable.EnableOptimizations"; const bool targetOutput = false; @@ -812,21 +1273,25 @@ public virtual void EmbeddingOfCustomReferenceTypeWithPropertyIsCorrect() using (var jsEngine = CreateJsEngine()) { jsEngine.EmbedHostType("BundleTable", bundleTableType); - jsEngine.Execute(updateCode); - output = jsEngine.Evaluate(input); + lock (BundleTable.SyncRoot) + { + jsEngine.Execute(updateCode); + output = jsEngine.Evaluate(input); + jsEngine.Execute(rollbackCode); + } } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } #endregion #region Types with methods - [Test] - public virtual void EmbeddingOfBuiltinValueTypeWithMethodIsCorrect() + [Fact] + public virtual void EmbeddingOfBuiltinValueTypeWithMethod() { // Arrange Type dateTimeType = typeof(DateTime); @@ -844,33 +1309,39 @@ public virtual void EmbeddingOfBuiltinValueTypeWithMethodIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EmbeddingOfBuiltinReferenceTypeWithMethodIsCorrect() + [Fact] + public virtual void EmbeddingOfBuiltinReferenceTypeWithMethods() { // Arrange Type mathType = typeof(Math); - const string input = "Math2.Max(5.37, 5.56)"; - const double targetOutput = 5.56; + const string input1 = "Math2.Max(5.37, 5.56)"; + const double targetOutput1 = 5.56; + + const string input2 = "Math2.Log10(23)"; + const double targetOutput2 = 1.36172783601759; // Act - double output; + double output1; + double output2; using (var jsEngine = CreateJsEngine()) { jsEngine.EmbedHostType("Math2", mathType); - output = jsEngine.Evaluate(input); + output1 = jsEngine.Evaluate(input1); + output2 = Math.Round(jsEngine.Evaluate(input2), 14); } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); } - [Test] - public virtual void EmbeddingOfCustomValueTypeWithMethodIsCorrect() + [Fact] + public virtual void EmbeddingOfCustomValueTypeWithMethod() { // Arrange var dateType = typeof(Date); @@ -888,11 +1359,11 @@ public virtual void EmbeddingOfCustomValueTypeWithMethodIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); } - [Test] - public virtual void EmbeddingOfCustomReferenceTypeWithMethodIsCorrect() + [Fact] + public virtual void EmbeddingOfCustomReferenceTypeWithMethod() { // Arrange Type base64EncoderType = typeof(Base64Encoder); @@ -910,7 +1381,84 @@ public virtual void EmbeddingOfCustomReferenceTypeWithMethodIsCorrect() } // Assert - Assert.AreEqual(targetOutput, output); + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void EmbeddingOfTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + Type type = typeof(Type); + string dateTimeTypeName = typeof(DateTime).FullName; + + string input = string.Format("Type.GetType(\"{0}\");", dateTimeTypeName); + string targetOutput = dateTimeTypeName; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("Type", type); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public virtual void EmbeddingOfAssemblyTypeAndCallingOfItsLoadMethod() + { + // Arrange + Type assemblyType = typeof(Assembly); + const string reflectionEmitAssemblyName = "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + + string input = string.Format("Assembly.Load(\"{0}\");", reflectionEmitAssemblyName); + const string targetOutput = reflectionEmitAssemblyName; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("Assembly", assemblyType); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + #endregion + + #region Removal + + [Fact] + public virtual void RemovingOfEmbeddedCustomReferenceType() + { + // Arrange + Type personType = typeof(Person); + + // Act + Exception currentException = null; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("Person", personType); + + try + { + jsEngine.RemoveVariable("Person"); + } + catch (Exception e) + { + currentException = e; + } + } + + // Assert + Assert.Null(currentException); } #endregion diff --git a/test/JavaScriptEngineSwitcher.Tests/IntlTestsBase.cs b/test/JavaScriptEngineSwitcher.Tests/IntlTestsBase.cs new file mode 100644 index 00000000..6744fd43 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/IntlTestsBase.cs @@ -0,0 +1,33 @@ +using Xunit; + +namespace JavaScriptEngineSwitcher.Tests +{ + public abstract class IntlTestsBase : TestsBase + { + [Fact] + public virtual void SupportsDateTimeFormatConstructor() + { + // Arrange + const string functionCode = @"function formatDate(value, locale) { + if (typeof value === 'string' && value.length > 0) { + value = new Date(value); + } + + return new Intl.DateTimeFormat(locale).format(value); +}"; + const string targetOutput = "16.09.2021"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(functionCode); + output = jsEngine.CallFunction("formatDate", "2021-09-16", "ru-ru"); + } + + // Assert + Assert.Equal(targetOutput, output); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/JavaScriptEngineSwitcher.Configuration.xsd b/test/JavaScriptEngineSwitcher.Tests/JavaScriptEngineSwitcher.Configuration.xsd deleted file mode 100644 index c562b13e..00000000 --- a/test/JavaScriptEngineSwitcher.Tests/JavaScriptEngineSwitcher.Configuration.xsd +++ /dev/null @@ -1,292 +0,0 @@ - - - - - Configuration settings of JavaScript Engine Switcher - - - - - - - - - - - - - - - - - - - - - - - - - Configuration settings of core - - - - - - - Name of default JavaScript engine - - - - - - - List of registered JavaScript engines - - - - - - - - - - - Adds JavaScript engine registration - - - - JavaScript engine name - - - - - JavaScript engine .NET-type name - - - - - - - Removes JavaScript engine registration - - - - JavaScript engine name - - - - - - - Removes all JavaScript engine registrations, that specified above - - - - - - - - Configuration settings of MSIE JavaScript engine - - - - Flag for whether to enable script debugging features (only works in the `ChakraIeJsRt` and `ChakraEdgeJsRt` modes) - - - - - JavaScript engine mode - - - - - - Automatically selects the most modern JavaScript engine from available on the machine - - - - - Classic MSIE JavaScript engine (supports ECMAScript 3 with possibility of using the ECMAScript 5 Polyfill and the JSON2 library). Requires Internet Explorer 6 or higher on the machine. - - - - - ActiveScript version of Chakra JavaScript engine (supports ECMAScript 3 with possibility of using the ECMAScript 5 Polyfill and the JSON2 library). Requires Internet Explorer 9 or higher on the machine. - - - - - “IE” JsRT version of Chakra JavaScript engine (supports ECMAScript 5). Requires Internet Explorer 11 or Microsoft Edge on the machine. - - - - - “Edge” JsRT version of Chakra JavaScript engine (supports ECMAScript 5). Requires Microsoft Edge on the machine. - - - - - - - - Flag for whether to use the ECMAScript 5 Polyfill - - - - - Flag for whether to use the JSON2 library - - - - - - - - - Configuration settings of V8 JavaScript engine - - - - Flag for whether to enable script debugging features (allows a TCP/IP-based debugging) - - - - - TCP/IP port on which to listen for a debugger connection - - - - - - - - - - - Flag for whether to disable global members - - - - - Maximum size of the new object heap in mebibytes - - - - - - - - - - - Maximum size of the old object heap in mebibytes - - - - - - - - - - - Maximum size of the executable code heap in mebibytes - - - - - - - - - - - - - - - Configuration settings of Jurassic JavaScript engine - - - - Flag for whether to enable script debugging features (allows a generation of debug information) - - - - - Flag for whether to disassemble any generated IL and store it in the associated function - - - - - Flag for whether to allow run the script in strict mode - - - - - - - - - Configuration settings of Jint JavaScript engine - - - - Flag for whether to allow the `debugger` statement to be called in a script - - - - - Flag for whether to enable debug mode - - - - - Maximum allowed depth of recursion: - -1 - recursion without limits; - N - one scope function can be called no more than N times. - - - - - - - - - - - Maximum number of statements - - - - - - - - - - - Flag for whether to allow run the script in strict mode - - - - - Number of milliseconds to wait before the script execution times out - - - - - - - - - - - \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/JavaScriptEngineSwitcher.Tests.csproj b/test/JavaScriptEngineSwitcher.Tests/JavaScriptEngineSwitcher.Tests.csproj index 21bcea31..3cc5b571 100644 --- a/test/JavaScriptEngineSwitcher.Tests/JavaScriptEngineSwitcher.Tests.csproj +++ b/test/JavaScriptEngineSwitcher.Tests/JavaScriptEngineSwitcher.Tests.csproj @@ -1,131 +1,76 @@ - - - + + - Debug - AnyCPU - {7C91107D-6DC4-41FC-B976-0C76E1DFD52A} + JS Engine Switcher: Tests + 3.30.2 + net462;net471;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0 Library - Properties - JavaScriptEngineSwitcher.Tests - JavaScriptEngineSwitcher.Tests - v4.0 - 512 - ..\ - true - Client - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + latest + true + true + true + false + true + false + + + - - {698a1aff-b84d-4fb1-b514-d18ffab5066d} - JavaScriptEngineSwitcher.ChakraCore - - - {5c903eef-bad1-43b8-bfe2-e4ee4d204410} - JavaScriptEngineSwitcher.Core - - - {26ece52e-991f-4e12-bb88-467201038eff} - JavaScriptEngineSwitcher.Jint - - - {2e667689-f072-401f-a9a5-09f1a2ed025c} - JavaScriptEngineSwitcher.Jurassic - - - {50ad3b1c-a295-42ac-979a-cd244429983c} - JavaScriptEngineSwitcher.Msie - - - {1baec601-b244-48d3-be27-351e133eef73} - JavaScriptEngineSwitcher.V8 - + + + + + + + + + + + + + + + + + + + + - - - ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - Designer - - + + - - - - - + + Files/%(RecursiveDir)/%(Filename)%(Extension) + PreserveNewest + PreserveNewest + - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jint/CommonTests.cs b/test/JavaScriptEngineSwitcher.Tests/Jint/CommonTests.cs index ceeb7150..b16e9260 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Jint/CommonTests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Jint/CommonTests.cs @@ -1,14 +1,566 @@ -namespace JavaScriptEngineSwitcher.Tests.Jint -{ - using Core; +using System; + +using Xunit; +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Jint; + +namespace JavaScriptEngineSwitcher.Tests.Jint +{ public class CommonTests : CommonTestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName + { + get { return "JintJsEngine"; } + } + + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Invalid or unexpected token", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(5, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var variable3 = 678; + +$variable1 + -variable2 - variable3;"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("variable2 is not defined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(5, exception.LineNumber); + Assert.Equal(15, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal(" at Global code (variables.js:5:15)", exception.CallStack); + } + + [Fact] + public void MappingCompilationErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(2%); +factorial(0);"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token ')'", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(13, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(-1); +factorial(0);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The value must be greater than or equal to zero.", exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(9, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at factorial (factorial.js:3:9)" + Environment.NewLine + + " at Global code (factorial.js:10:1)", + exception.CallStack + ); + } + + [Fact] + public void MappingRuntimeErrorDuringOutOfMemory() + { + // Arrange + const string input = @"var arr = []; + +for (var i = 0; i < 10000; i++) { + arr.push('Current date: ' + new Date()); +}"; + + JsRuntimeException exception = null; + + // Act + using (IJsEngine jsEngine = new JintJsEngine( + new JintSettings + { + MemoryLimit = 2 * 1024 * 1024 + } + )) + { + try + { + jsEngine.Execute(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Matches(@"^Script has allocated \d+ but is limited to 2097152$", exception.Description); + } + + [Fact] + public void MappingRuntimeErrorDuringArraySizeExceeded() + { + // Arrange + const string input = @"var arr = new Array(1000000000);"; + + JsRuntimeException exception = null; + + // Act + using (IJsEngine jsEngine = new JintJsEngine( + new JintSettings + { + MaxArraySize = 1_000_000 + } + )) + { + try + { + jsEngine.Execute(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The array size 1000000000 is larger than maximum allowed (1000000)", exception.Description); + } + + [Fact] + public void MappingCompilationErrorDuringMaxJsonParseDepthReached() + { + // Arrange + const string input = @"var data = '{\n' + + ' ""menu"": {\n' + + ' ""id"": ""file"",\n' + + ' ""value"": ""File"",\n' + + ' ""popup"": {\n' + + ' ""menuItem"": [\n' + + ' { ""value"": ""New"", ""onclick"": ""CreateNewDoc()"" },\n' + + ' { ""value"": ""Open"", ""onclick"": ""OpenDoc()"" },\n' + + ' { ""value"": ""Close"", ""onclick"": ""CloseDoc()"" }\n' + + ' ]\n' + + ' }\n' + + ' }\n' + + '}' + ; + +JSON.parse(data);"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = new JintJsEngine( + new JintSettings + { + MaxJsonParseDepth = 4 + } + )) + { + try + { + jsEngine.Execute(input, "menu.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Max. depth level of JSON reached at position 82", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("menu.js", exception.DocumentName); + Assert.Equal(16, exception.LineNumber); + Assert.Equal(1, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringRecursionDepthOverflow() { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("JintJsEngine"); + // Arrange + const string input = @"function fibonacci(n) { + if (n === 1) { + return 1; + } + else if (n === 2) { + return 1; + } + else { + return fibonacci(n - 1) + fibonacci(n - 2); + } +} + +(function (fibonacci) { + var a = 5; + var b = 11; + var c = fibonacci(b) - fibonacci(a); +})(fibonacci);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = new JintJsEngine( + new JintSettings + { + MaxRecursionDepth = 5 + } + )) + { + try + { + jsEngine.Execute(input, "fibonacci.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The recursion is forbidden by script host.", exception.Description); + Assert.Equal("RangeError", exception.Type); + Assert.Empty(exception.DocumentName); + Assert.Equal(0, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at fibonacci" + Environment.NewLine + + " at fibonacci" + Environment.NewLine + + " at fibonacci" + Environment.NewLine + + " at fibonacci" + Environment.NewLine + + " at fibonacci" + Environment.NewLine + + " at fibonacci" + Environment.NewLine + + " at Anonymous function", + exception.CallStack + ); + } + + [Fact] + public void MappingRuntimeErrorDuringStatementsCountOverflow() + { + // Arrange + const string input = @"while (true);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = new JintJsEngine( + new JintSettings + { + MaxStatements = 5 + } + )) + { + try + { + jsEngine.Execute(input, "infinite-loop.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The maximum number of statements executed have been reached.", exception.Description); + Assert.Equal("RangeError", exception.Type); + Assert.Empty(exception.DocumentName); + Assert.Equal(0, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Empty(exception.CallStack); + } + + [Fact] + public void MappingTimeoutErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"while (true);"; + + JsTimeoutException exception = null; + + // Act + using (var jsEngine = new JintJsEngine( + new JintSettings + { + TimeoutInterval = TimeSpan.FromMilliseconds(30) + } + )) + { + try + { + jsEngine.Execute(input, "infinite-loop.js"); + } + catch (JsTimeoutException e) + { + exception = e; + } + } - return jsEngine; + // Assert + Assert.NotNull(exception); + Assert.Equal("Timeout error", exception.Category); + Assert.Equal("Script execution exceeded timeout.", exception.Description); + Assert.Empty(exception.Type); + Assert.Empty(exception.DocumentName); + Assert.Equal(0, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Empty(exception.CallStack); } + + [Fact] + public void MappingTimeoutErrorDuringRegexHanging() + { + // Arrange + const string input = @"var regexp = /^(\w+\s?)*$/, + str = 'An input string that takes a long time or even makes this regular expression to hang!' + ; + +// Will take a very long time +regexp.test(str);"; + + JsTimeoutException exception = null; + + // Act + using (var jsEngine = new JintJsEngine( + new JintSettings + { + RegexTimeoutInterval = TimeSpan.FromMilliseconds(25) + } + )) + { + try + { + jsEngine.Execute(input, "regexp-hanging.js"); + } + catch (JsTimeoutException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Timeout error", exception.Category); + Assert.Equal("Script execution exceeded timeout.", exception.Description); + Assert.Empty(exception.Type); + Assert.Empty(exception.DocumentName); + Assert.Equal(0, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Empty(exception.CallStack); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"var arr = []; +var obj = {}; +var foo = 'Browser's bar';"; + string targetOutput = "SyntaxError: Unexpected identifier 's'" + Environment.NewLine + + " at variables.js:3:20" + ; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function foo(x, y) { + var z = x + y; + if (z > 20) { + bar(); + } +} + +(function (foo) { + var a = 8; + var b = 15; + + foo(a, b); +})(foo);"; + string targetOutput = "ReferenceError: bar is not defined" + Environment.NewLine + + " at foo (functions.js:4:3)" + Environment.NewLine + + " at Anonymous function (functions.js:12:2)" + Environment.NewLine + + " at Global code (functions.js:13:2)" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "functions.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jint/Es2015Tests.cs b/test/JavaScriptEngineSwitcher.Tests/Jint/Es2015Tests.cs new file mode 100644 index 00000000..89acb932 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Jint/Es2015Tests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Jint +{ + public class Es2015Tests : Es2015TestsBase + { + protected override string EngineName + { + get { return "JintJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jint/Es5Tests.cs b/test/JavaScriptEngineSwitcher.Tests/Jint/Es5Tests.cs index 463cd183..350699ff 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Jint/Es5Tests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Jint/Es5Tests.cs @@ -1,63 +1,12 @@ -namespace JavaScriptEngineSwitcher.Tests.Jint -{ - using NUnit.Framework; - - using Core; +using Xunit; +namespace JavaScriptEngineSwitcher.Tests.Jint +{ public class Es5Tests : Es5TestsBase { - protected override IJsEngine CreateJsEngine() - { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("JintJsEngine"); - - return jsEngine; - } - - #region Object methods - - [Test] - public override void ObjectKeysMethodIsSupported() + protected override string EngineName { - // Arrange - const string input1 = "Object.keys(['a', 'b', 'c']).toString();"; - const string targetOutput1 = "0,1,2"; - - const string input2 = "Object.keys({ 0: 'a', 1: 'b', 2: 'c' }).toString();"; - const string targetOutput2 = "0,1,2"; - - const string input3 = "Object.keys({ 100: 'a', 2: 'b', 7: 'c' }).toString();"; - const string targetOutput3 = "100,2,7"; - - const string input4A = @"var myObj = function() { }; -myObj.prototype = { getFoo: { value: function () { return this.foo } } };; -myObj.foo = 1; -"; - const string input4B = "Object.keys(myObj).toString();"; - const string targetOutput4 = "foo"; - - // Act - string output1; - string output2; - string output3; - string output4; - - using (var jsEngine = CreateJsEngine()) - { - output1 = jsEngine.Evaluate(input1); - output2 = jsEngine.Evaluate(input2); - output3 = jsEngine.Evaluate(input3); - - jsEngine.Execute(input4A); - output4 = jsEngine.Evaluate(input4B); - } - - // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); - Assert.AreEqual(targetOutput4, output4); + get { return "JintJsEngine"; } } - - #endregion } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jint/EvalTests.cs b/test/JavaScriptEngineSwitcher.Tests/Jint/EvalTests.cs new file mode 100644 index 00000000..e8868575 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Jint/EvalTests.cs @@ -0,0 +1,65 @@ +using Xunit; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Jint; + +namespace JavaScriptEngineSwitcher.Tests.Jint +{ + public class EvalTests : EvalTestsBase + { + protected override string EngineName + { + get { return "JintJsEngine"; } + } + + + private IJsEngine CreateJsEngine(bool disableEval) + { + var jsEngine = new JintJsEngine(new JintSettings + { + DisableEval = disableEval + }); + + return jsEngine; + } + + + public override void UsageOfEvalFunction() + { + // Arrange + int TestDisableEvalSetting(bool disableEval) + { + using (var jsEngine = CreateJsEngine(disableEval: disableEval)) + { + return jsEngine.Evaluate("eval('2*2');"); + } + } + + // Act and Assert + Assert.Equal(4, TestDisableEvalSetting(false)); + + JsRuntimeException exception = Assert.Throws(() => TestDisableEvalSetting(true)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("String compilation has been disabled in engine options", exception.Description); + } + + public override void UsageOfFunctionConstructor() + { + // Arrange + int TestDisableEvalSetting(bool disableEval) + { + using (var jsEngine = CreateJsEngine(disableEval: disableEval)) + { + return jsEngine.Evaluate("new Function('return 2*2;')();"); + } + } + + // Act and Assert + Assert.Equal(4, TestDisableEvalSetting(false)); + + JsRuntimeException exception = Assert.Throws(() => TestDisableEvalSetting(true)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("String compilation has been disabled in engine options", exception.Description); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jint/InteropTests.cs b/test/JavaScriptEngineSwitcher.Tests/Jint/InteropTests.cs index 79bfea56..3e39ebb2 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Jint/InteropTests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Jint/InteropTests.cs @@ -1,14 +1,514 @@ -namespace JavaScriptEngineSwitcher.Tests.Jint -{ - using Core; +using System; +using System.IO; +using System.Reflection; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Jint; + +using JavaScriptEngineSwitcher.Tests.Interop; +using JavaScriptEngineSwitcher.Tests.Interop.Animals; +namespace JavaScriptEngineSwitcher.Tests.Jint +{ public class InteropTests : InteropTestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName + { + get { return "JintJsEngine"; } + } + + + private IJsEngine CreateJsEngine(bool allowReflection) { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("JintJsEngine"); + var jsEngine = new JintJsEngine(new JintSettings + { + AllowReflection = allowReflection + }); return jsEngine; } + + #region Embedding of objects + + #region Objects with methods + + [Fact] + public override void EmbeddingOfInstanceOfCustomValueTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var date = new Date(); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("date", date); + return jsEngine.Evaluate("date.GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(Date).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Property 'GetType' of object is not a function", exception.Description); + } + + [Fact] + public override void EmbeddingOfInstanceOfCustomReferenceTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var cat = new Cat(); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("cat", cat); + return jsEngine.Evaluate("cat.GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(Cat).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Property 'GetType' of object is not a function", exception.Description); + } + + [Fact] + public override void EmbeddingOfInstanceOfAssemblyTypeAndCallingOfItsCreateInstanceMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Assembly assembly = this.GetType().Assembly; + string personTypeName = typeof(Person).FullName; + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("assembly", assembly); + return jsEngine.Evaluate("assembly.CreateInstance(\"" + personTypeName + "\");"); + } + } + + // Act and Assert + Assert.Equal("{FirstName=,LastName=}", TestAllowReflectionSetting(true)); + Assert.Equal("{FirstName=,LastName=}", TestAllowReflectionSetting(false)); + } + + #endregion + + #region Delegates + + [Fact] + public override void EmbeddingOfInstanceOfDelegateAndCallingItWithMissingParameter() + { + // Arrange + var sumFunc = new Func((a, b) => a + b); + + const string input = "sum(678)"; + const int targetOutput = 678; + + // Act + int output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("sum", sumFunc); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public override void EmbeddingOfInstanceOfDelegateAndGettingItsMethodProperty() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var cat = new Cat(); + var cryFunc = new Func(cat.Cry); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("cry", cryFunc); + return jsEngine.Evaluate("cry.Method;"); + } + } + + // Act and Assert + Assert.Equal("undefined", TestAllowReflectionSetting(true)); + Assert.Equal("undefined", TestAllowReflectionSetting(false)); + } + + #endregion + + #region Recursive calls + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringRecursiveEvaluationOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-evaluation/compilation-error"; + const string input = "evaluateFile('index').calculateResult();"; + + // Act + JsCompilationException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Func evaluateFile = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("evaluateFile", evaluateFile); + double output = jsEngine.Evaluate(input); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token ','", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("math.js", exception.DocumentName); + Assert.Equal(25, exception.LineNumber); + Assert.Equal(11, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringRecursiveEvaluationOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-evaluation/runtime-error"; + const string input = "evaluateFile('index').calculateResult();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Func evaluateFile = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("evaluateFile", evaluateFile); + double output = jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("argumens is not defined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("math.js", exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(14, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at sum (math.js:10:14)" + Environment.NewLine + + " at calculateResult (index.js:7:13)" + Environment.NewLine + + " at Global code (Script Document:1:1)", + exception.CallStack + ); + } + + [Fact] + public void MappingHostErrorDuringRecursiveEvaluationOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-evaluation/host-error"; + const string input = "evaluateFile('index').calculateResult();"; + + // Act + FileNotFoundException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Func evaluateFile = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("evaluateFile", evaluateFile); + double output = jsEngine.Evaluate(input); + } + catch (FileNotFoundException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.StartsWith("Could not find file '", exception.Message); + } + + [Fact] + public void MappingCompilationErrorDuringRecursiveExecutionOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-execution/compilation-error"; + const string variableName = "num"; + + // Act + JsCompilationException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Action executeFile = path => jsEngine.ExecuteFile(path); + + jsEngine.SetVariableValue("directoryPath", directoryPath); + jsEngine.EmbedHostObject("executeFile", executeFile); + jsEngine.ExecuteFile(Path.Combine(directoryPath, "main-file.js")); + + int output = jsEngine.GetVariableValue(variableName); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Invalid or unexpected token", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("second-file.js", exception.DocumentName); + Assert.Equal(1, exception.LineNumber); + Assert.Equal(6, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringRecursiveExecutionOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-execution/runtime-error"; + const string variableName = "num"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Action executeFile = path => jsEngine.ExecuteFile(path); + + jsEngine.SetVariableValue("directoryPath", directoryPath); + jsEngine.EmbedHostObject("executeFile", executeFile); + jsEngine.ExecuteFile(Path.Combine(directoryPath, "main-file.js")); + + int output = jsEngine.GetVariableValue(variableName); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("nuм is not defined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("second-file.js", exception.DocumentName); + Assert.Equal(1, exception.LineNumber); + Assert.Equal(1, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at Global code (second-file.js:1:1)" + Environment.NewLine + + " at Global code (first-file.js:2:1)" + Environment.NewLine + + " at Global code (main-file.js:2:1)", + exception.CallStack + ); + } + + [Fact] + public void MappingHostErrorDuringRecursiveExecutionOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-execution/host-error"; + const string variableName = "num"; + + // Act + FileNotFoundException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Action executeFile = path => jsEngine.ExecuteFile(path); + + jsEngine.SetVariableValue("directoryPath", directoryPath); + jsEngine.EmbedHostObject("executeFile", executeFile); + jsEngine.ExecuteFile(Path.Combine(directoryPath, "main-file.js")); + + int output = jsEngine.GetVariableValue(variableName); + } + catch (FileNotFoundException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("File '" + directoryPath + "/second-file.jsx' not exist.", exception.Message); + } + + #endregion + + #endregion + + #endregion + + + #region Embedding of types + + #region Creating of instances + + [Fact] + public override void CreatingAnInstanceOfEmbeddedBuiltinExceptionAndGettingItsTargetSiteProperty() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Type invalidOperationExceptionType = typeof(InvalidOperationException); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("InvalidOperationError", invalidOperationExceptionType); + return jsEngine.Evaluate("new InvalidOperationError(\"A terrible thing happened!\").TargetSite;"); + } + } + + // Act and Assert + Assert.Null(TestAllowReflectionSetting(true)); +#if NETFRAMEWORK + Assert.Null(TestAllowReflectionSetting(false)); +#else + Assert.Equal("undefined", TestAllowReflectionSetting(false)); +#endif + } + + [Fact] + public override void CreatingAnInstanceOfEmbeddedCustomExceptionAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Type loginFailedExceptionType = typeof(LoginFailedException); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("LoginFailedError", loginFailedExceptionType); + return jsEngine.Evaluate("new LoginFailedError(\"Wrong password entered!\").GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(LoginFailedException).FullName, TestAllowReflectionSetting(true)); +#if NETFRAMEWORK + Assert.Equal(typeof(LoginFailedException).FullName, TestAllowReflectionSetting(false)); +#else + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Property 'GetType' of object is not a function", exception.Description); +#endif + } + + #endregion + + #region Types with methods + + [Fact] + public override void EmbeddingOfTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string dateTimeTypeName = typeof(DateTime).FullName; + + string TestAllowReflectionSetting(bool allowReflection) + { + Type type = typeof(Type); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("Type", type); + return jsEngine.Evaluate("Type.GetType(\"" + dateTimeTypeName + "\");"); + } + } + + // Act and Assert + Assert.Equal(dateTimeTypeName, TestAllowReflectionSetting(true)); + Assert.Equal(dateTimeTypeName, TestAllowReflectionSetting(false)); + } + + [Fact] + public override void EmbeddingOfAssemblyTypeAndCallingOfItsLoadMethod() + { + // Arrange + const string reflectionEmitAssemblyName = "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + + string TestAllowReflectionSetting(bool allowReflection) + { + Type assemblyType = typeof(Assembly); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("Assembly", assemblyType); + return jsEngine.Evaluate("Assembly.Load(\"" + reflectionEmitAssemblyName + "\");"); + } + } + + // Act and Assert + Assert.Equal(reflectionEmitAssemblyName, TestAllowReflectionSetting(true)); + Assert.Equal(reflectionEmitAssemblyName, TestAllowReflectionSetting(false)); + } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jint/MultithreadingTests.cs b/test/JavaScriptEngineSwitcher.Tests/Jint/MultithreadingTests.cs new file mode 100644 index 00000000..5c6fb452 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Jint/MultithreadingTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Jint +{ + public class MultithreadingTests : MultithreadingTestsBase + { + protected override string EngineName + { + get { return "JintJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jint/PrecompilationTests.cs b/test/JavaScriptEngineSwitcher.Tests/Jint/PrecompilationTests.cs new file mode 100644 index 00000000..0e5a512e --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Jint/PrecompilationTests.cs @@ -0,0 +1,209 @@ +using System; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests.Jint +{ + public class PrecompilationTests : PrecompilationTestsBase + { + protected override string EngineName + { + get { return "JintJsEngine"; } + } + + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringPrecompilationOfCode() + { + // Arrange + const string input = @"function guid() { + function s4() { + return Math.floor((1 + Math.random() * 0x10000) + .toString(16) + .substring(1) + ; + } + + var result = s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + + return result; +}"; + + IPrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "guid.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token ';'", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("guid.js", exception.DocumentName); + Assert.Equal(6, exception.LineNumber); + Assert.Equal(4, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfPrecompiledCode() + { + // Arrange + const string input = @"function getItem(items, itemIndex) { + var item = items[itemIndex]; + + return item; +} + +(function (getItem) { + var items = null, + item = getItem(items, 5) + ; + + return item; +})(getItem);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + IPrecompiledScript precompiledScript = jsEngine.Precompile(input, "get-item.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Cannot read property '5' of null", exception.Description); + Assert.Equal("TypeError", exception.Type); + Assert.Equal("get-item.js", exception.DocumentName); + Assert.Equal(2, exception.LineNumber); + Assert.Equal(19, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at getItem (get-item.js:2:19)" + Environment.NewLine + + " at Anonymous function (get-item.js:9:10)" + Environment.NewLine + + " at Global code (get-item.js:13:2)", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"function makeId(length) { + var result = '', + possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + charIndex + ; + + for (charIndex = 0; charIndex < length; charIndex++) + result += possible.charAt(Math.floor(Math.random() * possible.length)); + } + + return result; +}"; + string targetOutput = "SyntaxError: Unexpected token '}'" + Environment.NewLine + + " at make-id.js:12:1" + ; + + IPrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "make-id.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function getFullName(firstName, lastName) { + var fullName = firstName + ' ' + middleName + ' ' + lastName; + + return fullName; +} + +(function (getFullName) { + var firstName = 'Vasya', + lastName = 'Pupkin' + ; + + return getFullName(firstName, lastName); +})(getFullName);"; + string targetOutput = "ReferenceError: middleName is not defined" + Environment.NewLine + + " at getFullName (get-full-name.js:2:35)" + Environment.NewLine + + " at Anonymous function (get-full-name.js:12:9)" + Environment.NewLine + + " at Global code (get-full-name.js:13:2)" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + IPrecompiledScript precompiledScript = jsEngine.Precompile(input, "get-full-name.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/JsEngineSwitcherInitializer.cs b/test/JavaScriptEngineSwitcher.Tests/JsEngineSwitcherInitializer.cs new file mode 100644 index 00000000..12a61590 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/JsEngineSwitcherInitializer.cs @@ -0,0 +1,49 @@ +#if NETCOREAPP +using System.Text; + +#endif +using JavaScriptEngineSwitcher.ChakraCore; +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Utilities; +using JavaScriptEngineSwitcher.Jint; +using JavaScriptEngineSwitcher.Jurassic; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.NiL; +using JavaScriptEngineSwitcher.Node; +using JavaScriptEngineSwitcher.V8; +using JavaScriptEngineSwitcher.Vroom; +using JavaScriptEngineSwitcher.Yantra; + +namespace JavaScriptEngineSwitcher.Tests +{ + internal static class JsEngineSwitcherInitializer + { + private static InterlockedStatedFlag _initializedFlag = new InterlockedStatedFlag(); + + + public static void Initialize() + { + if (_initializedFlag.Set()) + { +#if NETCOREAPP + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + +#endif + JsEngineSwitcher.Current.EngineFactories + .AddChakraCore() + .AddJint() + .AddJurassic() + .AddMsie(new MsieSettings + { + EngineMode = JsEngineMode.ChakraIeJsRt + }) + .AddNiL() + .AddNode() + .AddV8() + .AddVroom() + .AddYantra() + ; + } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jurassic/CommonTests.cs b/test/JavaScriptEngineSwitcher.Tests/Jurassic/CommonTests.cs index d1b6258a..dffebf29 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Jurassic/CommonTests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Jurassic/CommonTests.cs @@ -1,14 +1,260 @@ -namespace JavaScriptEngineSwitcher.Tests.Jurassic -{ - using Core; +using System; + +using Xunit; +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests.Jurassic +{ public class CommonTests : CommonTestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName + { + get { return "JurassicJsEngine"; } + } + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected character '@'.", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var variable3 = 678; + +$variable1 + -variable2 - variable3;"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("variable2 is not defined.", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(5, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal(" at Global code (variables.js:5)", exception.CallStack); + } + + [Fact] + public void MappingCompilationErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(2%); +factorial(0);"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Wrong number of operands", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(-1); +factorial(0);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The value must be greater than or equal to zero.", exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at factorial (factorial.js:3)" + Environment.NewLine + + " at Global code (factorial.js:10)", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"var arr = []; +var obj = {}; +var foo = 'Browser's bar';"; + string targetOutput = "SyntaxError: Expected operator but found 's'" + Environment.NewLine + + " at variables.js:3" + ; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("JurassicJsEngine"); + // Arrange + const string input = @"function foo(x, y) { + var z = x + y; + if (z > 20) { + bar(); + } +} + +(function (foo) { + var a = 8; + var b = 15; + + foo(a, b); +})(foo);"; + string targetOutput = "ReferenceError: bar is not defined." + Environment.NewLine + + " at Global code (functions.js:4)" + ; + + JsRuntimeException exception = null; - return jsEngine; + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "functions.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jurassic/Es2015Tests.cs b/test/JavaScriptEngineSwitcher.Tests/Jurassic/Es2015Tests.cs new file mode 100644 index 00000000..85c03267 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Jurassic/Es2015Tests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Jurassic +{ + public class Es2015Tests : Es2015TestsBase + { + protected override string EngineName + { + get { return "JurassicJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jurassic/Es5Tests.cs b/test/JavaScriptEngineSwitcher.Tests/Jurassic/Es5Tests.cs index 633c9c66..b84ac8bc 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Jurassic/Es5Tests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Jurassic/Es5Tests.cs @@ -1,22 +1,18 @@ -namespace JavaScriptEngineSwitcher.Tests.Jurassic -{ - using NUnit.Framework; - - using Core; +using Xunit; +namespace JavaScriptEngineSwitcher.Tests.Jurassic +{ public class Es5Tests : Es5TestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("JurassicJsEngine"); - - return jsEngine; + get { return "JurassicJsEngine"; } } #region Object methods - [Test] - public override void ObjectKeysMethodIsSupported() + [Fact] + public override void SupportsObjectKeysMethod() { // Arrange const string input1 = "Object.keys(['a', 'b', 'c']).toString();"; @@ -33,7 +29,7 @@ public override void ObjectKeysMethodIsSupported() myObj.foo = 1; "; const string input4B = "Object.keys(myObj).toString();"; - const string targetOutput4 = "displayName,foo"; + const string targetOutput4 = "foo"; // Act string output1; @@ -52,10 +48,10 @@ public override void ObjectKeysMethodIsSupported() } // Assert - Assert.AreEqual(targetOutput1, output1); - Assert.AreEqual(targetOutput2, output2); - Assert.AreEqual(targetOutput3, output3); - Assert.AreEqual(targetOutput4, output4); + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); + Assert.Equal(targetOutput4, output4); } #endregion diff --git a/test/JavaScriptEngineSwitcher.Tests/Jurassic/EvalTests.cs b/test/JavaScriptEngineSwitcher.Tests/Jurassic/EvalTests.cs new file mode 100644 index 00000000..128121d8 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Jurassic/EvalTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Jurassic +{ + public class EvalTests : EvalTestsBase + { + protected override string EngineName + { + get { return "JurassicJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jurassic/InteropTests.cs b/test/JavaScriptEngineSwitcher.Tests/Jurassic/InteropTests.cs index 28802068..0250db5d 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Jurassic/InteropTests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Jurassic/InteropTests.cs @@ -1,14 +1,68 @@ -namespace JavaScriptEngineSwitcher.Tests.Jurassic -{ - using Core; +using System; + +using Xunit; +namespace JavaScriptEngineSwitcher.Tests.Jurassic +{ public class InteropTests : InteropTestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName + { + get { return "JurassicJsEngine"; } + } + + #region Embedding of objects + + #region Objects with properties + + public override void EmbeddingOfInstanceOfAnonymousTypeWithProperties() + { } + + #endregion + + #region Delegates + + public override void EmbeddingOfInstanceOfDelegateAndCallingItWithMissingParameter() { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("JurassicJsEngine"); + // Arrange + var sumFunc = new Func((a, b) => a + b); + + const string input = "sum(678)"; + const int targetOutput = 678; - return jsEngine; + // Act + int output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("sum", sumFunc); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); } + + #endregion + + #endregion + + + #region Embedding of types + + #region Types with constants + + public override void EmbeddingOfBuiltinReferenceTypeWithConstants() + { } + + public override void EmbeddingOfCustomValueTypeWithConstants() + { } + + public override void EmbeddingOfCustomReferenceTypeWithConstant() + { } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jurassic/MultithreadingTests.cs b/test/JavaScriptEngineSwitcher.Tests/Jurassic/MultithreadingTests.cs new file mode 100644 index 00000000..4368c920 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Jurassic/MultithreadingTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Jurassic +{ + public class MultithreadingTests : MultithreadingTestsBase + { + protected override string EngineName + { + get { return "JurassicJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Jurassic/PrecompilationTests.cs b/test/JavaScriptEngineSwitcher.Tests/Jurassic/PrecompilationTests.cs new file mode 100644 index 00000000..778d611e --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Jurassic/PrecompilationTests.cs @@ -0,0 +1,205 @@ +using System; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests.Jurassic +{ + public class PrecompilationTests : PrecompilationTestsBase + { + protected override string EngineName + { + get { return "JurassicJsEngine"; } + } + + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringPrecompilationOfCode() + { + // Arrange + const string input = @"function guid() { + function s4() { + return Math.floor((1 + Math.random() * 0x10000) + .toString(16) + .substring(1) + ; + } + + var result = s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + + return result; +}"; + + IPrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "guid.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Missing closing token ')'", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("guid.js", exception.DocumentName); + Assert.Equal(6, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfPrecompiledCode() + { + // Arrange + const string input = @"function getItem(items, itemIndex) { + var item = items[itemIndex]; + + return item; +} + +(function (getItem) { + var items = null, + item = getItem(items, 5) + ; + + return item; +})(getItem);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + IPrecompiledScript precompiledScript = jsEngine.Precompile(input, "get-item.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("null cannot be converted to an object", exception.Description); + Assert.Equal("TypeError", exception.Type); + Assert.Equal("get-item.js", exception.DocumentName); + Assert.Equal(2, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at getItem (get-item.js:2)", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"function makeId(length) { + var result = '', + possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + charIndex + ; + + for (charIndex = 0; charIndex < length; charIndex++) + result += possible.charAt(Math.floor(Math.random() * possible.length)); + } + + return result; +}"; + string targetOutput = "SyntaxError: Return statements are only allowed inside functions" + Environment.NewLine + + " at make-id.js:11" + ; + + IPrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "make-id.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function getFullName(firstName, lastName) { + var fullName = firstName + ' ' + middleName + ' ' + lastName; + + return fullName; +} + +(function (getFullName) { + var firstName = 'Vasya', + lastName = 'Pupkin' + ; + + return getFullName(firstName, lastName); +})(getFullName);"; + string targetOutput = "ReferenceError: middleName is not defined." + Environment.NewLine + + " at Global code (get-full-name.js:2)" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + IPrecompiledScript precompiledScript = jsEngine.Precompile(input, "get-full-name.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Msie/CommonTests.cs b/test/JavaScriptEngineSwitcher.Tests/Msie/CommonTests.cs index 3109cb7f..2a2021e0 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Msie/CommonTests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Msie/CommonTests.cs @@ -1,14 +1,263 @@ -namespace JavaScriptEngineSwitcher.Tests.Msie -{ - using Core; +using System; + +using Xunit; +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests.Msie +{ public class CommonTests : CommonTestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("MsieJsEngine"); + get { return "MsieJsEngine"; } + } + + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Invalid character", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Empty(exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(5, exception.ColumnNumber); + Assert.Equal("var @variable3 = 678;", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var variable3 = 678; + +$variable1 + -variable2 - variable3;"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("'variable2' is undefined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(5, exception.LineNumber); + Assert.Equal(1, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal(" at Global code (variables.js:5:1)", exception.CallStack); + } + + [Fact] + public void MappingCompilationErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(2%); +factorial(0);"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } - return jsEngine; + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Syntax error", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Empty(exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(13, exception.ColumnNumber); + Assert.Equal("factorial(2%);", exception.SourceFragment); } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(-1); +factorial(0);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The value must be greater than or equal to zero.", exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(3, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at factorial (factorial.js:3:3)" + Environment.NewLine + + " at Global code (factorial.js:10:1)", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"var arr = []; +var obj = {}; +var foo = 'Browser's bar';"; + string targetOutput = "SyntaxError: Expected ';'" + Environment.NewLine + + " at 3:20 -> var foo = 'Browser's bar';" + ; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function foo(x, y) { + var z = x + y; + if (z > 20) { + bar(); + } +} + +(function (foo) { + var a = 8; + var b = 15; + + foo(a, b); +})(foo);"; + string targetOutput = "ReferenceError: 'bar' is undefined" + Environment.NewLine + + " at foo (functions.js:4:3)" + Environment.NewLine + + " at Anonymous function (functions.js:12:2)" + Environment.NewLine + + " at Global code (functions.js:8:2)" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "functions.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Msie/Es5Tests.cs b/test/JavaScriptEngineSwitcher.Tests/Msie/Es5Tests.cs index 723fd79f..9a387b29 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Msie/Es5Tests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Msie/Es5Tests.cs @@ -1,14 +1,10 @@ namespace JavaScriptEngineSwitcher.Tests.Msie { - using Core; - public class Es5Tests : Es5TestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("MsieJsEngine"); - - return jsEngine; + get { return "MsieJsEngine"; } } } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Msie/EvalTests.cs b/test/JavaScriptEngineSwitcher.Tests/Msie/EvalTests.cs new file mode 100644 index 00000000..ce0ec5fd --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Msie/EvalTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Msie +{ + public class EvalTests : EvalTestsBase + { + protected override string EngineName + { + get { return "MsieJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Msie/InteropTests.cs b/test/JavaScriptEngineSwitcher.Tests/Msie/InteropTests.cs index 32ceac32..e2848349 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Msie/InteropTests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/Msie/InteropTests.cs @@ -1,14 +1,290 @@ -namespace JavaScriptEngineSwitcher.Tests.Msie -{ - using Core; +using System; +using System.IO; +using System.Reflection; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Msie; +using JavaScriptEngineSwitcher.Tests.Interop; +using JavaScriptEngineSwitcher.Tests.Interop.Animals; + +namespace JavaScriptEngineSwitcher.Tests.Msie +{ public class InteropTests : InteropTestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("MsieJsEngine"); + get { return "MsieJsEngine"; } + } + + + private IJsEngine CreateJsEngine(bool allowReflection) + { + var jsEngine = new MsieJsEngine(new MsieSettings + { + AllowReflection = allowReflection, + EngineMode = JsEngineMode.ChakraIeJsRt + }); return jsEngine; } + + #region Embedding of objects + + #region Objects with methods + + [Fact] + public override void EmbeddingOfInstanceOfCustomValueTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var date = new Date(); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("date", date); + return jsEngine.Evaluate("date.GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(Date).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Object doesn't support property or method 'GetType'", exception.Description); + } + + [Fact] + public override void EmbeddingOfInstanceOfCustomReferenceTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var cat = new Cat(); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("cat", cat); + return jsEngine.Evaluate("cat.GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(Cat).FullName, TestAllowReflectionSetting(true)); + + JsRuntimeException exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Object doesn't support property or method 'GetType'", exception.Description); + } + + [Fact] + public override void EmbeddingOfInstanceOfAssemblyTypeAndCallingOfItsCreateInstanceMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Assembly assembly = this.GetType().Assembly; + string personTypeName = typeof(Person).FullName; + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("assembly", assembly); + return jsEngine.Evaluate("assembly.CreateInstance(\"" + personTypeName + "\");"); + } + } + + // Act and Assert + Assert.Equal("{FirstName=,LastName=}", TestAllowReflectionSetting(true)); + Assert.Equal("{FirstName=,LastName=}", TestAllowReflectionSetting(false)); + } + + #endregion + + #region Delegates + + [Fact] + public override void EmbeddingOfInstanceOfDelegateAndGettingItsMethodProperty() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var cat = new Cat(); + var cryFunc = new Func(cat.Cry); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("cry", cryFunc); + return jsEngine.Evaluate("cry.Method;"); + } + } + + // Act and Assert + Assert.Equal("undefined", TestAllowReflectionSetting(true)); + Assert.Equal("undefined", TestAllowReflectionSetting(false)); + } + + #endregion + + #region Recursive calls + + #region Mapping of errors + + [Fact] + public void MappingRuntimeErrorDuringRecursiveEvaluationOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-evaluation/runtime-error"; + const string input = "evaluateFile('index').calculateResult();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Func evaluateFile = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("evaluateFile", evaluateFile); + double output = jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("'argumens' is undefined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("math.js", exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(4, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at sum (math.js:10:4)" + Environment.NewLine + + " at calculateResult (index.js:7:4)" + Environment.NewLine + + " at Global code (Script Document:1:1)", + exception.CallStack + ); + } + + #endregion + + #endregion + + #endregion + + + #region Embedding of types + + #region Creating of instances + + [Fact] + public override void CreatingAnInstanceOfEmbeddedBuiltinExceptionAndGettingItsTargetSiteProperty() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Type invalidOperationExceptionType = typeof(InvalidOperationException); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("InvalidOperationError", invalidOperationExceptionType); + return jsEngine.Evaluate("new InvalidOperationError(\"A terrible thing happened!\").TargetSite;"); + } + } + + // Act and Assert + Assert.Null(TestAllowReflectionSetting(true)); + Assert.Equal("undefined", TestAllowReflectionSetting(false)); + } + + [Fact] + public override void CreatingAnInstanceOfEmbeddedCustomExceptionAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Type loginFailedExceptionType = typeof(LoginFailedException); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("LoginFailedError", loginFailedExceptionType); + return jsEngine.Evaluate("new LoginFailedError(\"Wrong password entered!\").GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(LoginFailedException).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Object doesn't support property or method 'GetType'", exception.Description); + } + + #endregion + + #region Types with methods + + [Fact] + public override void EmbeddingOfTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string dateTimeTypeName = typeof(DateTime).FullName; + + string TestAllowReflectionSetting(bool allowReflection) + { + Type type = typeof(Type); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("Type", type); + return jsEngine.Evaluate("Type.GetType(\"" + dateTimeTypeName + "\");"); + } + } + + // Act and Assert + Assert.Equal(dateTimeTypeName, TestAllowReflectionSetting(true)); + Assert.Equal(dateTimeTypeName, TestAllowReflectionSetting(false)); + } + + [Fact] + public override void EmbeddingOfAssemblyTypeAndCallingOfItsLoadMethod() + { + // Arrange + const string reflectionEmitAssemblyName = "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + + string TestAllowReflectionSetting(bool allowReflection) + { + Type assemblyType = typeof(Assembly); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("Assembly", assemblyType); + return jsEngine.Evaluate("Assembly.Load(\"" + reflectionEmitAssemblyName + "\");"); + } + } + + // Act and Assert + Assert.Equal(reflectionEmitAssemblyName, TestAllowReflectionSetting(true)); + Assert.Equal(reflectionEmitAssemblyName, TestAllowReflectionSetting(false)); + } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Msie/MultithreadingTests.cs b/test/JavaScriptEngineSwitcher.Tests/Msie/MultithreadingTests.cs new file mode 100644 index 00000000..b4c4e8b3 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Msie/MultithreadingTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Msie +{ + public class MultithreadingTests : MultithreadingTestsBase + { + protected override string EngineName + { + get { return "MsieJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Msie/PrecompilationTests.cs b/test/JavaScriptEngineSwitcher.Tests/Msie/PrecompilationTests.cs new file mode 100644 index 00000000..f810dc47 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Msie/PrecompilationTests.cs @@ -0,0 +1,209 @@ +using System; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests.Msie +{ + public class PrecompilationTests : PrecompilationTestsBase + { + protected override string EngineName + { + get { return "MsieJsEngine"; } + } + + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringPrecompilationOfCode() + { + // Arrange + const string input = @"function guid() { + function s4() { + return Math.floor((1 + Math.random() * 0x10000) + .toString(16) + .substring(1) + ; + } + + var result = s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + + return result; +}"; + + IPrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "guid.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Expected ')'", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("guid.js", exception.DocumentName); + Assert.Equal(6, exception.LineNumber); + Assert.Equal(4, exception.ColumnNumber); + Assert.Equal(" ;", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfPrecompiledCode() + { + // Arrange + const string input = @"function getItem(items, itemIndex) { + var item = items[itemIndex]; + + return item; +} + +(function (getItem) { + var items = null, + item = getItem(items, 5) + ; + + return item; +})(getItem);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + IPrecompiledScript precompiledScript = jsEngine.Precompile(input, "get-item.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Unable to get property '5' of undefined or null reference", exception.Description); + Assert.Equal("TypeError", exception.Type); + Assert.Equal("get-item.js", exception.DocumentName); + Assert.Equal(2, exception.LineNumber); + Assert.Equal(2, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at getItem (get-item.js:2:2)" + Environment.NewLine + + " at Anonymous function (get-item.js:9:3)" + Environment.NewLine + + " at Global code (get-item.js:7:2)", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"function makeId(length) { + var result = '', + possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + charIndex + ; + + for (charIndex = 0; charIndex < length; charIndex++) + result += possible.charAt(Math.floor(Math.random() * possible.length)); + } + + return result; +}"; + string targetOutput = "SyntaxError: 'return' statement outside of function" + Environment.NewLine + + " at make-id.js:11:2 -> return result;" + ; + + IPrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "make-id.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function getFullName(firstName, lastName) { + var fullName = firstName + ' ' + middleName + ' ' + lastName; + + return fullName; +} + +(function (getFullName) { + var firstName = 'Vasya', + lastName = 'Pupkin' + ; + + return getFullName(firstName, lastName); +})(getFullName);"; + string targetOutput = "ReferenceError: 'middleName' is undefined" + Environment.NewLine + + " at getFullName (get-full-name.js:2:2)" + Environment.NewLine + + " at Anonymous function (get-full-name.js:12:2)" + Environment.NewLine + + " at Global code (get-full-name.js:7:2)" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + IPrecompiledScript precompiledScript = jsEngine.Precompile(input, "get-full-name.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/MultithreadingTestsBase.cs b/test/JavaScriptEngineSwitcher.Tests/MultithreadingTestsBase.cs new file mode 100644 index 00000000..e739acfb --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/MultithreadingTestsBase.cs @@ -0,0 +1,36 @@ +using System.Threading; + +using Xunit; + +namespace JavaScriptEngineSwitcher.Tests +{ + public abstract class MultithreadingTestsBase : TestsBase + { + [Fact] + public virtual void ExecutionOfCodeFromDifferentThreads() + { + // Arrange + const string variableName = "foo"; + string inputCode1 = string.Format("var {0} = 'bar';", variableName); + string inputCode2 = string.Format("{0} = 'baz';", variableName); + const string targetOutput = "baz"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(inputCode1); + + var thread = new Thread(() => jsEngine.Execute(inputCode2)); + thread.Start(); + thread.Join(); + + output = jsEngine.GetVariableValue(variableName); + } + + // Assert + Assert.Equal(targetOutput, output); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/NiL/CommonTests.cs b/test/JavaScriptEngineSwitcher.Tests/NiL/CommonTests.cs new file mode 100644 index 00000000..58ddd8b2 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/NiL/CommonTests.cs @@ -0,0 +1,265 @@ +using System; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests.NiL +{ + public class CommonTests : CommonTestsBase + { + protected override string EngineName + { + get { return "NiLJsEngine"; } + } + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Invalid variable definition", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Empty(exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(5, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var variable3 = 678; + +$variable1 + -variable2 - variable3;"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Variable \"variable2\" is not defined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Empty(exception.DocumentName); + Assert.Equal(5, exception.LineNumber); + Assert.Equal(15, exception.ColumnNumber); + Assert.Equal("$variable1 + -variable2 - variable3;", exception.SourceFragment); + Assert.Equal(" at Global code (5:15)", exception.CallStack); + } + + [Fact] + public void MappingCompilationErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(2%); +factorial(0);"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Empty(exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(13, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(-1); +factorial(0);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The value must be greater than or equal to zero.", exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Empty(exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(3, exception.ColumnNumber); + Assert.Equal( + " throw new Error(\"The value must be greater than or equal to zero.\");", + exception.SourceFragment + ); + Assert.Equal( + " at factorial (3:3)" + Environment.NewLine + + " at Global code (10:1)", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"var arr = []; +var obj = {}; +var foo = 'Browser's bar';"; + string targetOutput = "SyntaxError: Unexpected token 's'" + Environment.NewLine + + " at 3:20" + ; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function foo(x, y) { + var z = x + y; + if (z > 20) { + bar(); + } +} + +(function (foo) { + var a = 8; + var b = 15; + + foo(a, b); +})(foo);"; + string targetOutput = "ReferenceError: Variable \"bar\" is not defined" + Environment.NewLine + + " at foo (4:3) -> bar();" + Environment.NewLine + + " at Anonymous function (12:2)" + Environment.NewLine + + " at Global code (8:1)" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "functions.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/NiL/Es5Tests.cs b/test/JavaScriptEngineSwitcher.Tests/NiL/Es5Tests.cs new file mode 100644 index 00000000..d83a291b --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/NiL/Es5Tests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.NiL +{ + public class Es5Tests : Es5TestsBase + { + protected override string EngineName + { + get { return "NiLJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/NiL/EvalTests.cs b/test/JavaScriptEngineSwitcher.Tests/NiL/EvalTests.cs new file mode 100644 index 00000000..1eab545c --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/NiL/EvalTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.NiL +{ + public class EvalTests : EvalTestsBase + { + protected override string EngineName + { + get { return "NiLJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/NiL/InteropTests.cs b/test/JavaScriptEngineSwitcher.Tests/NiL/InteropTests.cs new file mode 100644 index 00000000..304db66e --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/NiL/InteropTests.cs @@ -0,0 +1,177 @@ +using System; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +using JavaScriptEngineSwitcher.Tests.Interop; +using JavaScriptEngineSwitcher.Tests.Interop.Animals; + +namespace JavaScriptEngineSwitcher.Tests.NiL +{ + public class InteropTests : InteropTestsBase + { + protected override string EngineName + { + get { return "NiLJsEngine"; } + } + + + #region Embedding of objects + + #region Objects with methods + + [Fact] + public override void EmbeddingOfInstanceOfCustomValueTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + var date = new Date(); + + const string input = "date.GetType();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.EmbedHostObject("date", date); + jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("date.GetType is not a function", exception.Description); + } + + [Fact] + public override void EmbeddingOfInstanceOfCustomReferenceTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + var cat = new Cat(); + + const string input = @"cat.GetType();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.EmbedHostObject("cat", cat); + jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("cat.GetType is not a function", exception.Description); + } + + #endregion + + #region Delegates + + [Fact] + public override void EmbeddingOfInstanceOfDelegateAndCallingItWithMissingParameter() + { + // Arrange + var sumFunc = new Func((a, b) => a + b); + + const string input = "sum(678)"; + const int targetOutput = 678; + + // Act + int output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("sum", sumFunc); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + #endregion + + #endregion + + + #region Embedding of types + + #region Creating of instances + + [Fact] + public override void CreatingAnInstanceOfEmbeddedBuiltinExceptionAndGettingItsTargetSiteProperty() + { + // Arrange + Type invalidOperationExceptionType = typeof(InvalidOperationException); + + const string input = "new InvalidOperationError(\"A terrible thing happened!\").TargetSite;"; + + // Act + string output; + const string targetOutput = "undefined"; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("InvalidOperationError", invalidOperationExceptionType); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + #endregion + + #region Types with methods + + [Fact] + public override void EmbeddingOfBuiltinReferenceTypeWithMethods() + { + // Arrange + Type mathType = typeof(Math); + + const string input1 = "Math2.Max(5.37, 5.56)"; + const double targetOutput1 = 5.56; + + const string input2 = "Math2.Log10(23)"; + const double targetOutput2 = 1.36172783601759; + + // Act + double output1; + double output2; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("Math2", mathType); + output1 = Math.Round(jsEngine.Evaluate(input1), 2); + output2 = Math.Round(jsEngine.Evaluate(input2), 14); + } + + // Assert + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/NiL/MultithreadingTests.cs b/test/JavaScriptEngineSwitcher.Tests/NiL/MultithreadingTests.cs new file mode 100644 index 00000000..d926a2f2 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/NiL/MultithreadingTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.NiL +{ + public class MultithreadingTests : MultithreadingTestsBase + { + protected override string EngineName + { + get { return "NiLJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/NiL/PrecompilationTests.cs b/test/JavaScriptEngineSwitcher.Tests/NiL/PrecompilationTests.cs new file mode 100644 index 00000000..72953641 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/NiL/PrecompilationTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.NiL +{ + public class PrecompilationTests : PrecompilationTestsBase + { + protected override string EngineName + { + get { return "NiLJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Node/BuiltInLibraryTests.cs b/test/JavaScriptEngineSwitcher.Tests/Node/BuiltInLibraryTests.cs new file mode 100644 index 00000000..e1c86e18 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Node/BuiltInLibraryTests.cs @@ -0,0 +1,63 @@ +using Xunit; + +using JavaScriptEngineSwitcher.Node; + +namespace JavaScriptEngineSwitcher.Tests.Node +{ + public class BuiltinLibraryTests : TestsBase + { + protected override string EngineName + { + get { return "NodeJsEngine"; } + } + + + [Fact] + public void AccessingToRequireFunction() + { + // Arrange + var withoutBuiltinLibrary = new NodeSettings { UseBuiltinLibrary = false }; + var withBuiltinLibrary = new NodeSettings { UseBuiltinLibrary = true }; + + const string input = @"typeof require !== 'undefined';"; + + // Act + bool output1 = false; + bool output2 = false; + + using (var jsEngine = new NodeJsEngine(withoutBuiltinLibrary)) + { + output1 = jsEngine.Evaluate(input); + } + + using (var jsEngine = new NodeJsEngine(withBuiltinLibrary)) + { + output2 = jsEngine.Evaluate(input); + } + + // Assert + Assert.False(output1); + Assert.True(output2); + } + + [Fact] + public void ReadingOfFile() + { + // Arrange + const string input = @"let fs = require('fs'); +fs.readFileSync('Files/link.txt', 'utf8')"; + const string targetOutput = "http://www.panopticoncentral.net/2015/09/09/the-two-faces-of-jsrt-in-windows-10/"; + + // Act + string output = string.Empty; + + using (var jsEngine = new NodeJsEngine(new NodeSettings { UseBuiltinLibrary = true })) + { + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Node/CommonTests.cs b/test/JavaScriptEngineSwitcher.Tests/Node/CommonTests.cs new file mode 100644 index 00000000..472234cf --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Node/CommonTests.cs @@ -0,0 +1,425 @@ +using System; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Node; + +namespace JavaScriptEngineSwitcher.Tests.Node +{ + public class CommonTests : CommonTestsBase + { + protected override string EngineName + { + get { return "NodeJsEngine"; } + } + + + #region Evaluation of scripts + + [Fact] + public override void EvaluationOfExpressionWithUndefinedResult() + { } + + [Fact] + public override void EvaluationOfExpressionWithNullResult() + { + // Arrange + const string input = "null"; + const string targetOutput = "null"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + #endregion + + #region Calling of functions + + [Fact] + public override void CallingOfFunctionWithoutParameters() + { + // Arrange + const string functionCode = @"function hooray() { + return 'Hooray!'; +}"; + const string targetOutput = "Hooray!"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(functionCode); + output = jsEngine.CallFunction("hooray"); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public override void CallingOfFunctionWithUndefinedResult() + { } + + [Fact] + public override void CallingOfFunctionWithNullResult() + { + // Arrange + const string functionCode = @"function testNull(value) { + if (value !== null) { + throw new TypeError(); + } + + return null; +}"; + const object input = null; + const string targetOutput = "null"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(functionCode); + output = jsEngine.CallFunction("testNull", input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public override void CallingOfFunctionWithManyParameters() + { } + + #endregion + + #region Getting, setting and removing variables + + [Fact] + public override void SettingAndGettingVariableWithUndefinedValue() + { } + + [Fact] + public override void SettingAndGettingVariableWithNullValue() + { + // Arrange + const string variableName = "myVar2"; + const object input = null; + const string targetOutput = "null"; + + // Act + bool variableExists; + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.SetVariableValue(variableName, input); + variableExists = jsEngine.HasVariable(variableName); + output = jsEngine.GetVariableValue(variableName); + } + + // Assert + Assert.True(variableExists); + Assert.Equal(targetOutput, output); + } + + #endregion + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Invalid or unexpected token", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(5, exception.ColumnNumber); + Assert.Equal("var @variable3 = 678;", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var variable3 = 678; + +$variable1 + -variable2 - variable3;"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("variable2 is not defined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(5, exception.LineNumber); + Assert.Equal(15, exception.ColumnNumber); + Assert.Equal("$variable1 + -variable2 - variable3;", exception.SourceFragment); + Assert.Equal(" at variables.js:5:15", exception.CallStack); + } + + [Fact] + public void MappingCompilationErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(2%); +factorial(0);"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token ')'", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(13, exception.ColumnNumber); + Assert.Equal("factorial(2%);", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(-1); +factorial(0);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The value must be greater than or equal to zero.", exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(9, exception.ColumnNumber); + Assert.Equal( + " throw new Error(\"The value must be greater than or equal to zero.\");", + exception.SourceFragment + ); + Assert.Equal( + " at factorial (factorial.js:3:9)" + Environment.NewLine + + " at factorial.js:10:1", + exception.CallStack + ); + } + + [Fact] + public void MappingTimeoutErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"while (true);"; + + JsTimeoutException exception = null; + + // Act + using (var jsEngine = new NodeJsEngine( + new NodeSettings + { + TimeoutInterval = TimeSpan.FromMilliseconds(30) + } + )) + { + try + { + jsEngine.Execute(input, "infinite-loop.js"); + } + catch (JsTimeoutException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Timeout error", exception.Category); + Assert.Equal("Script execution exceeded timeout.", exception.Description); + Assert.Empty(exception.Type); + Assert.Empty(exception.DocumentName); + Assert.Equal(0, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Empty(exception.CallStack); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"var arr = []; +var obj = {}; +var foo = 'Browser's bar';"; + string targetOutput = "SyntaxError: Unexpected identifier 's'" + Environment.NewLine + + " at variables.js:3:20 -> var foo = 'Browser's bar';" + ; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function foo(x, y) { + var z = x + y; + if (z > 20) { + bar(); + } +} + +(function (foo) { + var a = 8; + var b = 15; + + foo(a, b); +})(foo);"; + string targetOutput = "ReferenceError: bar is not defined" + Environment.NewLine + + " at foo (functions.js:4:3) -> bar();" + Environment.NewLine + + " at functions.js:12:2" + Environment.NewLine + + " at functions.js:13:3" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "functions.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Node/Es2015Tests.cs b/test/JavaScriptEngineSwitcher.Tests/Node/Es2015Tests.cs new file mode 100644 index 00000000..0e2a7d18 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Node/Es2015Tests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Node +{ + public class Es2015Tests : Es2015TestsBase + { + protected override string EngineName + { + get { return "NodeJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Node/Es5Tests.cs b/test/JavaScriptEngineSwitcher.Tests/Node/Es5Tests.cs new file mode 100644 index 00000000..7e49ac74 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Node/Es5Tests.cs @@ -0,0 +1,18 @@ +namespace JavaScriptEngineSwitcher.Tests.Node +{ + public class Es5Tests : Es5TestsBase + { + protected override string EngineName + { + get { return "NodeJsEngine"; } + } + + + #region Object methods + + public override void SupportsObjectCreateMethod() + { } + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Node/EvalTests.cs b/test/JavaScriptEngineSwitcher.Tests/Node/EvalTests.cs new file mode 100644 index 00000000..645e2662 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Node/EvalTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Node +{ + public class EvalTests : EvalTestsBase + { + protected override string EngineName + { + get { return "NodeJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Node/MultithreadingTests.cs b/test/JavaScriptEngineSwitcher.Tests/Node/MultithreadingTests.cs new file mode 100644 index 00000000..5ed8c5ae --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Node/MultithreadingTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Node +{ + public class MultithreadingTests : MultithreadingTestsBase + { + protected override string EngineName + { + get { return "NodeJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Node/PrecompilationTests.cs b/test/JavaScriptEngineSwitcher.Tests/Node/PrecompilationTests.cs new file mode 100644 index 00000000..43f974d9 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Node/PrecompilationTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Node +{ + public class PrecompilationTests : PrecompilationTestsBase + { + protected override string EngineName + { + get { return "NodeJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Node/SecurityTests.cs b/test/JavaScriptEngineSwitcher.Tests/Node/SecurityTests.cs new file mode 100644 index 00000000..bdc3212e --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Node/SecurityTests.cs @@ -0,0 +1,40 @@ +using Xunit; + +namespace JavaScriptEngineSwitcher.Tests.Node +{ + public class SecurityTests : TestsBase + { + protected override string EngineName + { + get { return "NodeJsEngine"; } + } + + + [Fact] + public void AccessingToProcess() + { + // Arrange + const string input1 = @"typeof process === 'undefined';"; + const string input2 = @"let process = this.constructor.constructor('return this.process;')(); +typeof process === 'undefined';"; + + // Act + bool output1 = false; + bool output2 = false; + + using (var jsEngine = CreateJsEngine()) + { + output1 = jsEngine.Evaluate(input1); + } + + using (var jsEngine = CreateJsEngine()) + { + output2 = jsEngine.Evaluate(input2); + } + + // Assert + Assert.True(output1); + Assert.True(output2); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/PrecompilationTestsBase.cs b/test/JavaScriptEngineSwitcher.Tests/PrecompilationTestsBase.cs new file mode 100644 index 00000000..4ca5aac9 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/PrecompilationTestsBase.cs @@ -0,0 +1,239 @@ +using System.Reflection; +using System.Threading.Tasks; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests +{ + public abstract class PrecompilationTestsBase : TestsBase + { + #region Execution of precompiled scripts + + [Fact] + public virtual void ExecutionOfPrecompiledCode() + { + // Arrange + const string libraryCode = @"function declensionOfNumerals(number, titles) { + var result, + 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 = titles[titleIndex]; + + return result; +} + +function declinationOfSeconds(number) { + return declensionOfNumerals(number, ['секунда', 'секунды', 'секунд']); +}"; + const string functionName = "declinationOfSeconds"; + const int itemCount = 4; + + int[] inputSeconds = new int[itemCount] { 0, 1, 42, 600 }; + string[] targetOutputStrings = new string[itemCount] { "секунд", "секунда", "секунды", "секунд" }; + string[] outputStrings = new string[itemCount]; + + // Act + bool supportsScriptPrecompilation = false; + IPrecompiledScript precompiledCode = null; + + using (var jsEngine = CreateJsEngine()) + { + supportsScriptPrecompilation = jsEngine.SupportsScriptPrecompilation; + if (supportsScriptPrecompilation) + { + precompiledCode = jsEngine.Precompile(libraryCode, "declination-of-seconds.js"); + + jsEngine.Execute(precompiledCode); + outputStrings[0] = jsEngine.CallFunction(functionName, inputSeconds[0]); + } + } + + if (supportsScriptPrecompilation) + { + Parallel.For(1, itemCount, itemIndex => + { + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(precompiledCode); + outputStrings[itemIndex] = jsEngine.CallFunction(functionName, inputSeconds[itemIndex]); + } + }); + } + + // Assert + if (supportsScriptPrecompilation) + { + for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) + { + Assert.Equal(targetOutputStrings[itemIndex], outputStrings[itemIndex]); + } + } + } + + [Fact] + public virtual void ExecutionOfPrecompiledFile() + { + // Arrange + const string filePath = "Files/declination-of-minutes.js"; + const string functionName = "declinationOfMinutes"; + const int itemCount = 4; + + int[] inputMinutes = new int[itemCount] { 0, 1, 22, 88 }; + string[] targetOutputStrings = new string[itemCount] { "минут", "минута", "минуты", "минут" }; + string[] outputStrings = new string[itemCount]; + + // Act + bool supportsScriptPrecompilation = false; + IPrecompiledScript precompiledFile = null; + + using (var jsEngine = CreateJsEngine()) + { + supportsScriptPrecompilation = jsEngine.SupportsScriptPrecompilation; + if (supportsScriptPrecompilation) + { + precompiledFile = jsEngine.PrecompileFile(filePath); + + jsEngine.Execute(precompiledFile); + outputStrings[0] = jsEngine.CallFunction(functionName, inputMinutes[0]); + } + } + + if (supportsScriptPrecompilation) + { + Parallel.For(1, itemCount, itemIndex => + { + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(precompiledFile); + outputStrings[itemIndex] = jsEngine.CallFunction(functionName, inputMinutes[itemIndex]); + } + }); + } + + // Assert + if (supportsScriptPrecompilation) + { + for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) + { + Assert.Equal(targetOutputStrings[itemIndex], outputStrings[itemIndex]); + } + } + } + + [Fact] + public virtual void ExecutionOfPrecompiledResourceByNameAndType() + { + // Arrange + const string resourceName = "Resources.declination-of-hours.js"; + const string functionName = "declinationOfHours"; + const int itemCount = 4; + + int[] inputHours = new int[itemCount] { 0, 1, 24, 48 }; + string[] targetOutputStrings = new string[itemCount] { "часов", "час", "часа", "часов" }; + string[] outputStrings = new string[itemCount]; + + // Act + bool supportsScriptPrecompilation = false; + IPrecompiledScript precompiledResource = null; + + using (var jsEngine = CreateJsEngine()) + { + supportsScriptPrecompilation = jsEngine.SupportsScriptPrecompilation; + if (supportsScriptPrecompilation) + { + precompiledResource = jsEngine.PrecompileResource(resourceName, typeof(PrecompilationTestsBase)); + + jsEngine.Execute(precompiledResource); + outputStrings[0] = jsEngine.CallFunction(functionName, inputHours[0]); + } + } + + if (supportsScriptPrecompilation) + { + Parallel.For(1, itemCount, itemIndex => + { + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(precompiledResource); + outputStrings[itemIndex] = jsEngine.CallFunction(functionName, inputHours[itemIndex]); + } + }); + } + + // Assert + if (supportsScriptPrecompilation) + { + for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) + { + Assert.Equal(targetOutputStrings[itemIndex], outputStrings[itemIndex]); + } + } + } + + [Fact] + public virtual void ExecutionOfPrecompiledResourceByNameAndAssembly() + { + // Arrange + const string resourceName = "JavaScriptEngineSwitcher.Tests.Resources.declination-of-days.js"; + const string functionName = "declinationOfDays"; + const int itemCount = 4; + + int[] inputDays = new int[itemCount] { 0, 1, 3, 80 }; + string[] targetOutputStrings = new string[itemCount] { "дней", "день", "дня", "дней" }; + string[] outputStrings = new string[itemCount]; + + // Act + bool supportsScriptPrecompilation = false; + IPrecompiledScript precompiledResource = null; + + using (var jsEngine = CreateJsEngine()) + { + supportsScriptPrecompilation = jsEngine.SupportsScriptPrecompilation; + if (supportsScriptPrecompilation) + { + precompiledResource = jsEngine.PrecompileResource(resourceName, + typeof(PrecompilationTestsBase).GetTypeInfo().Assembly); + + jsEngine.Execute(precompiledResource); + outputStrings[0] = jsEngine.CallFunction(functionName, inputDays[0]); + } + } + + if (supportsScriptPrecompilation) + { + Parallel.For(1, itemCount, itemIndex => + { + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(precompiledResource); + outputStrings[itemIndex] = jsEngine.CallFunction(functionName, inputDays[itemIndex]); + } + }); + } + + // Assert + if (supportsScriptPrecompilation) + { + for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) + { + Assert.Equal(targetOutputStrings[itemIndex], outputStrings[itemIndex]); + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Properties/AssemblyInfo.cs b/test/JavaScriptEngineSwitcher.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 128484ec..00000000 --- a/test/JavaScriptEngineSwitcher.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("JavaScriptEngineSwitcher.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JavaScript Engine Switcher for .Net: Tests")] -[assembly: AssemblyCopyright("Copyright © 2013-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("4cfa9f30-6718-4b7e-8329-7602278b8e31")] - -[assembly: AssemblyVersion("1.5.0.0")] -[assembly: AssemblyFileVersion("1.5.0.0")] \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Resources/cube.js b/test/JavaScriptEngineSwitcher.Tests/Resources/cube.js index 84c65634..8c6f12b7 100644 --- a/test/JavaScriptEngineSwitcher.Tests/Resources/cube.js +++ b/test/JavaScriptEngineSwitcher.Tests/Resources/cube.js @@ -1,3 +1,3 @@ function cube(num) { return num * num * num; -} +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Resources/declination-of-days.js b/test/JavaScriptEngineSwitcher.Tests/Resources/declination-of-days.js new file mode 100644 index 00000000..812f2cb7 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Resources/declination-of-days.js @@ -0,0 +1,23 @@ +function declensionOfNumerals(number, titles) { + var result, + 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 = titles[titleIndex]; + + return result; +} + +function declinationOfDays(number) { + return declensionOfNumerals(number, ['день', 'дня', 'дней']); +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Resources/declination-of-hours.js b/test/JavaScriptEngineSwitcher.Tests/Resources/declination-of-hours.js new file mode 100644 index 00000000..6660462e --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Resources/declination-of-hours.js @@ -0,0 +1,23 @@ +function declensionOfNumerals(number, titles) { + var result, + 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 = titles[titleIndex]; + + return result; +} + +function declinationOfHours(number) { + return declensionOfNumerals(number, ['час', 'часа', 'часов']); +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/TestsBase.cs b/test/JavaScriptEngineSwitcher.Tests/TestsBase.cs new file mode 100644 index 00000000..676284da --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/TestsBase.cs @@ -0,0 +1,26 @@ +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests +{ + public abstract class TestsBase + { + /// + /// Gets a name of JavaScript engine + /// + protected abstract string EngineName { get; } + + + static TestsBase() + { + JsEngineSwitcherInitializer.Initialize(); + } + + + public IJsEngine CreateJsEngine() + { + var jsEngine = JsEngineSwitcher.Current.CreateEngine(EngineName); + + return jsEngine; + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/V8/CommonTests.cs b/test/JavaScriptEngineSwitcher.Tests/V8/CommonTests.cs index a94cde12..e1453c02 100644 --- a/test/JavaScriptEngineSwitcher.Tests/V8/CommonTests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/V8/CommonTests.cs @@ -1,14 +1,300 @@ -namespace JavaScriptEngineSwitcher.Tests.V8 -{ - using Core; +using System; + +using Xunit; +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.V8; + +namespace JavaScriptEngineSwitcher.Tests.V8 +{ public class CommonTests : CommonTestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName + { + get { return "V8JsEngine"; } + } + + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Invalid or unexpected token", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(5, exception.ColumnNumber); + Assert.Equal("var @variable3 = 678;", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var variable3 = 678; + +$variable1 + -variable2 - variable3;"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("variable2 is not defined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(5, exception.LineNumber); + Assert.Equal(15, exception.ColumnNumber); + Assert.Equal("$variable1 + -variable2 - variable3;", exception.SourceFragment); + Assert.Equal(" at variables.js:5:15", exception.CallStack); + } + + [Fact] + public void MappingCompilationErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(2%); +factorial(0);"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token ')'", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(13, exception.ColumnNumber); + Assert.Equal("factorial(2%);", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(-1); +factorial(0);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The value must be greater than or equal to zero.", exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(9, exception.ColumnNumber); + Assert.Equal(" throw new Error(\"The value must be greater than or equal to zero.\");", exception.SourceFragment); + Assert.Equal( + " at factorial (factorial.js:3:9)" + Environment.NewLine + + " at factorial.js:10:1", + exception.CallStack + ); + } + + [Fact] + public void MappingRuntimeErrorDuringOutOfMemory() + { + // Arrange + const string input = @"var arr = []; + +for (var i = 0; i < 10000; i++) { + arr.push('Current date: ' + new Date()); +}"; + + JsRuntimeException exception = null; + + // Act + using (IJsEngine jsEngine = new V8JsEngine( + new V8Settings + { + MaxHeapSize = new UIntPtr(640 * 1024) + } + )) + { + try + { + jsEngine.Execute(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The V8 runtime has exceeded its memory limit", exception.Description); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"var arr = []; +var obj = {}; +var foo = 'Browser's bar';"; + string targetOutput = "SyntaxError: Unexpected identifier 's'" + Environment.NewLine + + " at variables.js:3:20 -> var foo = 'Browser's bar';" + ; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("V8JsEngine"); + // Arrange + const string input = @"function foo(x, y) { + var z = x + y; + if (z > 20) { + bar(); + } +} + +(function (foo) { + var a = 8; + var b = 15; + + foo(a, b); +})(foo);"; + string targetOutput = "ReferenceError: bar is not defined" + Environment.NewLine + + " at foo (functions.js:4:3) -> bar();" + Environment.NewLine + + " at functions.js:12:2" + Environment.NewLine + + " at functions.js:13:3" + ; - return jsEngine; + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "functions.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/V8/Es2015Tests.cs b/test/JavaScriptEngineSwitcher.Tests/V8/Es2015Tests.cs new file mode 100644 index 00000000..60d188a4 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/V8/Es2015Tests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.V8 +{ + public class Es2015Tests : Es2015TestsBase + { + protected override string EngineName + { + get { return "V8JsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/V8/Es5Tests.cs b/test/JavaScriptEngineSwitcher.Tests/V8/Es5Tests.cs index ae56e023..81264d18 100644 --- a/test/JavaScriptEngineSwitcher.Tests/V8/Es5Tests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/V8/Es5Tests.cs @@ -1,14 +1,10 @@ namespace JavaScriptEngineSwitcher.Tests.V8 { - using Core; - public class Es5Tests : Es5TestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("V8JsEngine"); - - return jsEngine; + get { return "V8JsEngine"; } } } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/V8/EvalTests.cs b/test/JavaScriptEngineSwitcher.Tests/V8/EvalTests.cs new file mode 100644 index 00000000..74e5de39 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/V8/EvalTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.V8 +{ + public class EvalTests : EvalTestsBase + { + protected override string EngineName + { + get { return "V8JsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/V8/InteropTests.cs b/test/JavaScriptEngineSwitcher.Tests/V8/InteropTests.cs index 6c7f9725..ded468d7 100644 --- a/test/JavaScriptEngineSwitcher.Tests/V8/InteropTests.cs +++ b/test/JavaScriptEngineSwitcher.Tests/V8/InteropTests.cs @@ -1,14 +1,233 @@ -namespace JavaScriptEngineSwitcher.Tests.V8 -{ - using Core; +using System; +using System.Reflection; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.V8; +using JavaScriptEngineSwitcher.Tests.Interop; +using JavaScriptEngineSwitcher.Tests.Interop.Animals; + +namespace JavaScriptEngineSwitcher.Tests.V8 +{ public class InteropTests : InteropTestsBase { - protected override IJsEngine CreateJsEngine() + protected override string EngineName + { + get { return "V8JsEngine"; } + } + + + private IJsEngine CreateJsEngine(bool allowReflection) { - var jsEngine = JsEngineSwitcher.Current.CreateJsEngineInstance("V8JsEngine"); + var jsEngine = new V8JsEngine(new V8Settings + { + AllowReflection = allowReflection + }); return jsEngine; } + + #region Embedding of objects + + #region Objects with methods + + public override void EmbeddingOfInstanceOfCustomValueTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var date = new Date(); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("date", date); + return jsEngine.Evaluate("date.GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(Date).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Use of reflection is prohibited in this script engine", exception.Description); + } + + public override void EmbeddingOfInstanceOfCustomReferenceTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var cat = new Cat(); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("cat", cat); + return jsEngine.Evaluate("cat.GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(Cat).FullName, TestAllowReflectionSetting(true)); + + JsRuntimeException exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Use of reflection is prohibited in this script engine", exception.Description); + } + + public override void EmbeddingOfInstanceOfAssemblyTypeAndCallingOfItsCreateInstanceMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Assembly assembly = this.GetType().Assembly; + string personTypeName = typeof(Person).FullName; + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("assembly", assembly); + return jsEngine.Evaluate("assembly.CreateInstance(\"" + personTypeName + "\");"); + } + } + + // Act and Assert + Assert.Equal("{FirstName=,LastName=}", TestAllowReflectionSetting(true)); + Assert.Equal("{FirstName=,LastName=}", TestAllowReflectionSetting(false)); + } + + #endregion + + #region Delegates + + public override void EmbeddingOfInstanceOfDelegateAndCheckingItsPrototype() + { } + + public override void EmbeddingOfInstanceOfDelegateAndGettingItsMethodProperty() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + var cat = new Cat(); + var cryFunc = new Func(cat.Cry); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostObject("cry", cryFunc); + return jsEngine.Evaluate("cry.Method;"); + } + } + + // Act and Assert + Assert.Equal("System.String Cry()", TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Use of reflection is prohibited in this script engine", exception.Description); + } + + #endregion + + #endregion + + + #region Embedding of types + + #region Creating of instances + + public override void CreatingAnInstanceOfEmbeddedBuiltinExceptionAndGettingItsTargetSiteProperty() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Type invalidOperationExceptionType = typeof(InvalidOperationException); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("InvalidOperationError", invalidOperationExceptionType); + return jsEngine.Evaluate("new InvalidOperationError(\"A terrible thing happened!\").TargetSite;"); + } + } + + // Act and Assert + Assert.Null(TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Use of reflection is prohibited in this script engine", exception.Description); + } + + public override void CreatingAnInstanceOfEmbeddedCustomExceptionAndCallingOfItsGetTypeMethod() + { + // Arrange + string TestAllowReflectionSetting(bool allowReflection) + { + Type loginFailedExceptionType = typeof(LoginFailedException); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("LoginFailedError", loginFailedExceptionType); + return jsEngine.Evaluate("new LoginFailedError(\"Wrong password entered!\").GetType();"); + } + } + + // Act and Assert + Assert.Equal(typeof(LoginFailedException).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Use of reflection is prohibited in this script engine", exception.Description); + } + + #endregion + + #region Types with methods + + public override void EmbeddingOfTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + string dateTimeTypeName = typeof(DateTime).FullName; + + string TestAllowReflectionSetting(bool allowReflection) + { + Type type = typeof(Type); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("Type", type); + return jsEngine.Evaluate("Type.GetType(\"" + dateTimeTypeName + "\");"); + } + } + + // Act and Assert + Assert.Equal(dateTimeTypeName, TestAllowReflectionSetting(true)); + Assert.Equal(dateTimeTypeName, TestAllowReflectionSetting(false)); + } + + public override void EmbeddingOfAssemblyTypeAndCallingOfItsLoadMethod() + { + // Arrange + const string reflectionEmitAssemblyName = "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + + string TestAllowReflectionSetting(bool allowReflection) + { + Type assemblyType = typeof(Assembly); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("Assembly", assemblyType); + return jsEngine.Evaluate("Assembly.Load(\"" + reflectionEmitAssemblyName + "\");"); + } + } + + // Act and Assert + Assert.Equal(reflectionEmitAssemblyName, TestAllowReflectionSetting(true)); + Assert.Equal(reflectionEmitAssemblyName, TestAllowReflectionSetting(false)); + } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/V8/IntlTests.cs b/test/JavaScriptEngineSwitcher.Tests/V8/IntlTests.cs new file mode 100644 index 00000000..68812190 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/V8/IntlTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.V8 +{ + public class IntlTests : IntlTestsBase + { + protected override string EngineName + { + get { return "V8JsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/V8/MultithreadingTests.cs b/test/JavaScriptEngineSwitcher.Tests/V8/MultithreadingTests.cs new file mode 100644 index 00000000..9661c84b --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/V8/MultithreadingTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.V8 +{ + public class MultithreadingTests : MultithreadingTestsBase + { + protected override string EngineName + { + get { return "V8JsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/V8/PrecompilationTests.cs b/test/JavaScriptEngineSwitcher.Tests/V8/PrecompilationTests.cs new file mode 100644 index 00000000..fb3f7152 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/V8/PrecompilationTests.cs @@ -0,0 +1,210 @@ +using System; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests.V8 +{ + public class PrecompilationTests : PrecompilationTestsBase + { + protected override string EngineName + { + get { return "V8JsEngine"; } + } + + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringPrecompilationOfCode() + { + // Arrange + const string input = @"function guid() { + function s4() { + return Math.floor((1 + Math.random() * 0x10000) + .toString(16) + .substring(1) + ; + } + + var result = s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + + return result; +}"; + + IPrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "guid.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("missing ) after argument list", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("guid.js", exception.DocumentName); + Assert.Equal(5, exception.LineNumber); + Assert.Equal(16, exception.ColumnNumber); + Assert.Equal(" .substring(1)", exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfPrecompiledCode() + { + // Arrange + const string input = @"function getItem(items, itemIndex) { + var item = items[itemIndex]; + + return item; +} + +(function (getItem) { + var items = null, + item = getItem(items, 5) + ; + + return item; +})(getItem);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + IPrecompiledScript precompiledScript = jsEngine.Precompile(input, "get-item.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Cannot read properties of null (reading '5')", exception.Description); + Assert.Equal("TypeError", exception.Type); + Assert.Equal("get-item.js", exception.DocumentName); + Assert.Equal(2, exception.LineNumber); + Assert.Equal(18, exception.ColumnNumber); + Assert.Equal(" var item = items[itemIndex];", exception.SourceFragment); + Assert.Equal( + " at getItem (get-item.js:2:18)" + Environment.NewLine + + " at get-item.js:9:10" + Environment.NewLine + + " at get-item.js:13:3", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"function makeId(length) { + var result = '', + possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + charIndex + ; + + for (charIndex = 0; charIndex < length; charIndex++) + result += possible.charAt(Math.floor(Math.random() * possible.length)); + } + + return result; +}"; + string targetOutput = "SyntaxError: Illegal return statement" + Environment.NewLine + + " at make-id.js:11:2 -> return result;" + ; + + IPrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "make-id.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function getFullName(firstName, lastName) { + var fullName = firstName + ' ' + middleName + ' ' + lastName; + + return fullName; +} + +(function (getFullName) { + var firstName = 'Vasya', + lastName = 'Pupkin' + ; + + return getFullName(firstName, lastName); +})(getFullName);"; + string targetOutput = "ReferenceError: middleName is not defined" + Environment.NewLine + + " at getFullName (get-full-name.js:2:35) -> var fullName = firstName + " + + "' ' + middleName + ' ' + lastName;" + Environment.NewLine + + " at get-full-name.js:12:9" + Environment.NewLine + + " at get-full-name.js:13:3" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + IPrecompiledScript precompiledScript = jsEngine.Precompile(input, "get-full-name.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/ValidationTests.cs b/test/JavaScriptEngineSwitcher.Tests/ValidationTests.cs new file mode 100644 index 00000000..00d4aa58 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/ValidationTests.cs @@ -0,0 +1,98 @@ +using Xunit; + +using JavaScriptEngineSwitcher.Core.Helpers; + +namespace JavaScriptEngineSwitcher.Tests +{ + public class ValidationTests + { + [Fact] + public void CheckingOfCorrectNameFormat() + { + // Arrange + + // Act + bool name1FormatIsCorrect = ValidationHelpers.CheckNameFormat("good_parts"); + bool name2FormatIsCorrect = ValidationHelpers.CheckNameFormat("i18n"); + bool name3FormatIsCorrect = ValidationHelpers.CheckNameFormat("fooBar"); + bool name4FormatIsCorrect = ValidationHelpers.CheckNameFormat("$grid"); + bool name5FormatIsCorrect = ValidationHelpers.CheckNameFormat("a"); + bool name6FormatIsCorrect = ValidationHelpers.CheckNameFormat("À_la_maison"); + + // Assert + Assert.True(name1FormatIsCorrect); + Assert.True(name2FormatIsCorrect); + Assert.True(name3FormatIsCorrect); + Assert.True(name4FormatIsCorrect); + Assert.True(name5FormatIsCorrect); + Assert.True(name6FormatIsCorrect); + } + + [Fact] + public void CheckingOfWrongNameFormat() + { + // Arrange + + // Act + bool name1FormatIsWrong = ValidationHelpers.CheckNameFormat("good-parts"); + bool name2FormatIsWrong = ValidationHelpers.CheckNameFormat("1sale"); + bool name3FormatIsWrong = ValidationHelpers.CheckNameFormat("Foo Bar"); + bool name4FormatIsWrong = ValidationHelpers.CheckNameFormat("@grid"); + bool name5FormatIsWrong = ValidationHelpers.CheckNameFormat("2"); + + // Assert + Assert.False(name1FormatIsWrong); + Assert.False(name2FormatIsWrong); + Assert.False(name3FormatIsWrong); + Assert.False(name4FormatIsWrong); + Assert.False(name5FormatIsWrong); + } + + [Fact] + public void CheckingOfCorrectDocumentNameFormat() + { + // Arrange + + // Act + bool documentName1FormatIsCorrect = ValidationHelpers.CheckDocumentNameFormat("Script Document"); + bool documentName2FormatIsCorrect = ValidationHelpers.CheckDocumentNameFormat("Script Document [2]"); + bool documentName3FormatIsCorrect = ValidationHelpers.CheckDocumentNameFormat("doc01.js"); + bool documentName4FormatIsCorrect = ValidationHelpers.CheckDocumentNameFormat("/res/scripts.min.js"); + bool documentName5FormatIsCorrect = ValidationHelpers.CheckDocumentNameFormat( + @"C:\Users\Vasya\AppData\Roaming\npm\node_modules\typescript\lib\tsc.js"); + bool documentName6FormatIsCorrect = ValidationHelpers.CheckDocumentNameFormat( + "BundleTransformer.Less.Resources.less-combined.min.js"); + + // Assert + Assert.True(documentName1FormatIsCorrect); + Assert.True(documentName2FormatIsCorrect); + Assert.True(documentName3FormatIsCorrect); + Assert.True(documentName4FormatIsCorrect); + Assert.True(documentName5FormatIsCorrect); + Assert.True(documentName6FormatIsCorrect); + } + + [Fact] + public void CheckingOfWrongDocumentNameFormat() + { + // Arrange + + // Act + bool documentName1FormatIsWrong = ValidationHelpers.CheckDocumentNameFormat("Script Document"); + bool documentName2FormatIsWrong = ValidationHelpers.CheckDocumentNameFormat("Script Document <2>"); + bool documentName3FormatIsWrong = ValidationHelpers.CheckDocumentNameFormat(" doc01.js"); + bool documentName4FormatIsWrong = ValidationHelpers.CheckDocumentNameFormat(@"Document ""Test"""); + bool documentName5FormatIsWrong = ValidationHelpers.CheckDocumentNameFormat("src/*.js"); + bool documentName6FormatIsWrong = ValidationHelpers.CheckDocumentNameFormat( + "/js/shared/SubScribeModal/subscribeChecker.js?v=2017-11-09"); + + // Assert + Assert.False(documentName1FormatIsWrong); + Assert.False(documentName2FormatIsWrong); + Assert.False(documentName3FormatIsWrong); + Assert.False(documentName4FormatIsWrong); + Assert.False(documentName5FormatIsWrong); + Assert.False(documentName6FormatIsWrong); + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Vroom/CommonTests.cs b/test/JavaScriptEngineSwitcher.Tests/Vroom/CommonTests.cs new file mode 100644 index 00000000..13c3d21d --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Vroom/CommonTests.cs @@ -0,0 +1,281 @@ +using System; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests.Vroom +{ + public class CommonTests : CommonTestsBase + { + protected override string EngineName + { + get { return "VroomJsEngine"; } + } + + + #region Evaluation of code + + [Fact] + public override void EvaluationOfExpressionWithUndefinedResult() + { } + + #endregion + + #region Calling of functions + + [Fact] + public override void CallingOfFunctionWithUndefinedResult() + { } + + #endregion + + #region Getting, setting and removing variables + + [Fact] + public override void SettingAndGettingVariableWithUndefinedValue() + { } + + #endregion + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token ILLEGAL", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(5, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var variable3 = 678; + +$variable1 + -variable2 - variable3;"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("variable2 is not defined", exception.Description); + Assert.Equal("ReferenceError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(5, exception.LineNumber); + Assert.Equal(15, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Empty(exception.CallStack); + } + + [Fact] + public void MappingCompilationErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(2%); +factorial(0);"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token )", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(13, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(-1); +factorial(0);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The value must be greater than or equal to zero.", exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(9, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Empty(exception.CallStack); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"var arr = []; +var obj = {}; +var foo = 'Browser's bar';"; + string targetOutput = "SyntaxError: Unexpected identifier" + Environment.NewLine + + " at variables.js:3:20" + ; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function foo(x, y) { + var z = x + y; + if (z > 20) { + bar(); + } +} + +(function (foo) { + var a = 8; + var b = 15; + + foo(a, b); +})(foo);"; + string targetOutput = "ReferenceError: bar is not defined" + Environment.NewLine + + " at functions.js:4:3" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "functions.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Vroom/Es5Tests.cs b/test/JavaScriptEngineSwitcher.Tests/Vroom/Es5Tests.cs new file mode 100644 index 00000000..6249d20c --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Vroom/Es5Tests.cs @@ -0,0 +1,18 @@ +namespace JavaScriptEngineSwitcher.Tests.Vroom +{ + public class Es5Tests : Es5TestsBase + { + protected override string EngineName + { + get { return "VroomJsEngine"; } + } + + + #region Object methods + + public override void SupportsObjectCreateMethod() + { } + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Vroom/EvalTests.cs b/test/JavaScriptEngineSwitcher.Tests/Vroom/EvalTests.cs new file mode 100644 index 00000000..0e4d3cbe --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Vroom/EvalTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Vroom +{ + public class EvalTests : EvalTestsBase + { + protected override string EngineName + { + get { return "VroomJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Vroom/InteropTests.cs b/test/JavaScriptEngineSwitcher.Tests/Vroom/InteropTests.cs new file mode 100644 index 00000000..5cc43336 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Vroom/InteropTests.cs @@ -0,0 +1,104 @@ +namespace JavaScriptEngineSwitcher.Tests.Vroom +{ + public class InteropTests : InteropTestsBase + { + protected override string EngineName + { + get { return "VroomJsEngine"; } + } + + + #region Embedding of objects + + #region Objects with fields + + public override void EmbeddingOfInstanceOfCustomValueTypeWithFields() + { } + + public override void EmbeddingOfInstanceOfCustomReferenceTypeWithFields() + { } + + #endregion + + #region Objects with methods + + public override void EmbeddingOfInstanceOfBuiltinReferenceTypeWithMethod() + { } + + public override void EmbeddingOfInstanceOfCustomReferenceTypeWithMethod() + { } + + public override void EmbeddingOfInstanceOfAssemblyTypeAndCallingOfItsCreateInstanceMethod() + { } + + #endregion + + #region Delegates + + public override void EmbeddingOfInstanceOfDelegateWithoutParameters() + { } + + public override void EmbeddingOfInstanceOfDelegateWithOneParameter() + { } + + public override void EmbeddingOfInstanceOfDelegateWithTwoParameters() + { } + + public override void EmbeddingOfInstanceOfDelegateWithoutResult() + { } + + public override void EmbeddingOfInstanceOfDelegateAndCheckingItsPrototype() + { } + + public override void EmbeddingOfInstanceOfDelegateAndCallingItWithMissingParameter() + { } + + public override void EmbeddingOfInstanceOfDelegateAndCallingItWithExtraParameter() + { } + + public override void EmbeddingOfInstanceOfDelegateAndGettingItsMethodProperty() + { } + + #endregion + + #region Recursive calls + + public override void RecursiveExecutionOfFiles() + { } + + public override void RecursiveEvaluationOfFiles() + { } + + #endregion + + #endregion + + + #region Embedding of types + + #region Types with fields + + public override void EmbeddingOfBuiltinValueTypeWithField() + { } + + public override void EmbeddingOfCustomReferenceTypeWithField() + { } + + #endregion + + #region Types with methods + + public override void EmbeddingOfBuiltinReferenceTypeWithMethods() + { } + + public override void EmbeddingOfTypeAndCallingOfItsGetTypeMethod() + { } + + public override void EmbeddingOfAssemblyTypeAndCallingOfItsLoadMethod() + { } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Vroom/MultithreadingTests.cs b/test/JavaScriptEngineSwitcher.Tests/Vroom/MultithreadingTests.cs new file mode 100644 index 00000000..ca4f2526 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Vroom/MultithreadingTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Vroom +{ + public class MultithreadingTests : MultithreadingTestsBase + { + protected override string EngineName + { + get { return "VroomJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Vroom/PrecompilationTests.cs b/test/JavaScriptEngineSwitcher.Tests/Vroom/PrecompilationTests.cs new file mode 100644 index 00000000..669b7b41 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Vroom/PrecompilationTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Vroom +{ + public class PrecompilationTests : PrecompilationTestsBase + { + protected override string EngineName + { + get { return "VroomJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Yantra/CommonTests.cs b/test/JavaScriptEngineSwitcher.Tests/Yantra/CommonTests.cs new file mode 100644 index 00000000..077244f9 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Yantra/CommonTests.cs @@ -0,0 +1,302 @@ +using System; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +namespace JavaScriptEngineSwitcher.Tests.Yantra +{ + public class CommonTests : CommonTestsBase + { + protected override string EngineName + { + get { return "YantraJsEngine"; } + } + + + #region Error handling + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 # 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token Hash: #", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(15, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringEvaluationOfExpression() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var variable3 = 678; + +$variable1 + _variable2() - variable3;"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("711 is not a function", exception.Description); + Assert.Equal("TypeError", exception.Type); + Assert.Equal("variables.js", exception.DocumentName); + Assert.Equal(5, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal(" at Global code (variables.js:5)", exception.CallStack); + } + + [Fact] + public void MappingCompilationErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(%2); +factorial(0);"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token Mod: %", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(10, exception.LineNumber); + Assert.Equal(10, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringExecutionOfCode() + { + // Arrange + const string input = @"function factorial(value) { + if (value <= 0) { + throw new Error(""The value must be greater than or equal to zero.""); + } + + return value !== 1 ? value * factorial(value - 1) : 1; +} + +factorial(5); +factorial(-1); +factorial(0);"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("The value must be greater than or equal to zero.", exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Equal("factorial.js", exception.DocumentName); + Assert.Equal(3, exception.LineNumber); + Assert.Equal(2, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at factorial (factorial.js:3:2)" + Environment.NewLine + + " at Global code (factorial.js:10)", + exception.CallStack + ); + } + + [Fact] + public void MappingRuntimeErrorDuringStackOverflow() + { + // Arrange + const string input = @"var i = 0; + +function recursive() { + i++; + recursive(); +} + +recursive();"; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "recursive.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Maximum call stack size exceeded", exception.Description); + Assert.Equal("RangeError", exception.Type); + Assert.Equal("recursive.js", exception.DocumentName); + Assert.Equal(5, exception.LineNumber); + Assert.Equal(1, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + #endregion + + #region Generation of error messages + + [Fact] + public void GenerationOfCompilationErrorMessage() + { + // Arrange + const string input = @"var arr = []; +var obj = {}; +var foo = 'Browser's bar;"; + string targetOutput = "SyntaxError: Undefined binary operation Identifier" + Environment.NewLine + + " at variables.js:3:1" + ; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + [Fact] + public void GenerationOfRuntimeErrorMessage() + { + // Arrange + const string input = @"function foo(x, y) { + var z = x + y; + if (z > 20) { + bar(); + } +} + +(function (foo) { + var a = 8; + var b = 15; + + foo(a, b); +})(foo);"; + string targetOutput = "TypeError: undefined is not a function" + Environment.NewLine + + " at foo (functions.js:4:2)" + Environment.NewLine + + " at Anonymous function (functions.js:12:1)" + Environment.NewLine + + " at Global code (functions.js:8)" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.Execute(input, "functions.js"); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.Equal(targetOutput, exception.Message); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Yantra/ConsoleTests.cs b/test/JavaScriptEngineSwitcher.Tests/Yantra/ConsoleTests.cs new file mode 100644 index 00000000..0a6ccdd7 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Yantra/ConsoleTests.cs @@ -0,0 +1,237 @@ +using System; +using System.Globalization; +using System.Text; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Yantra; + +namespace JavaScriptEngineSwitcher.Tests.Yantra +{ + public class ConsoleTests + { + private IJsEngine CreateJsEngine(YantraJsConsoleCallback consoleCallback) + { + var jsEngine = new YantraJsEngine(new YantraSettings + { + ConsoleCallback = consoleCallback + }); + + return jsEngine; + } + + [Fact] + public void SupportsConsoleLogMethod() + { + // Arrange + Type favoriteSchoolSubject = typeof(Math); + var wikipediaPageUrl = new Uri("https://ru.wikipedia.org/wiki/%D0%92%D0%B0%D1%81%D1%8F_" + + "%D0%9F%D1%83%D0%BF%D0%BA%D0%B8%D0%BD"); + var sb = new StringBuilder(); + var logger = new StringLogger(sb); + + const string input = @"var id = Symbol('id'), + name = 'Василий Пупкин', + address = { city: 'Тамбов', street: 'Магистральная', ""houseNumber"": '41к7', apartmentNumber: 115 }, + dateOfBirth = new Date(1990, 2, 15), + isSingle = true, + salary = 22000.82, + email = null, + website = undefined, + icq = 698426795, + pets = ['Мурзик', 'Шарик'] + ; + +console.log(website, email, address, pets, isSingle, icq, salary, name, id, dateOfBirth); + +function calculateIncomeTax(salary) { + var result = salary * 0.13; + + return result; +} + +console.log('Функция для расчета подоходного налога:', calculateIncomeTax); +console.log('Папа у Васи силен в', favoriteSchoolSubject); +console.log('Страница в Википедии:', wikipediaPageUrl);"; + string targetOutput = "undefined null {\"city\":\"Тамбов\",\"street\":\"Магистральная\"," + + "\"houseNumber\":\"41к7\",\"apartmentNumber\":115} [\"Мурзик\",\"Шарик\"] True 698426795 22000.82 " + + "Василий Пупкин Symbol(id) 1990-03-14T21:00:00.0000000Z" + Environment.NewLine + + "Функция для расчета подоходного налога: [Function: calculateIncomeTax]" + Environment.NewLine + + "Папа у Васи силен в System.Math" + Environment.NewLine + + "Страница в Википедии: https://ru.wikipedia.org/wiki/Вася_Пупкин" + Environment.NewLine + ; + + // Act + using (var jsEngine = CreateJsEngine(consoleCallback: logger.Log)) + { + jsEngine.EmbedHostType("favoriteSchoolSubject", favoriteSchoolSubject); + jsEngine.EmbedHostObject("wikipediaPageUrl", wikipediaPageUrl); + jsEngine.Execute(input); + } + + string output = sb.ToString(); + + logger.Dispose(); + sb.Clear(); + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public void SupportsConsoleInfoMethod() + { + // Arrange + var sb = new StringBuilder(); + var logger = new StringLogger(sb); + + const string input = @"var driveLetter = 'C', availableDiskSpace = 237; + +console.info('There are', availableDiskSpace, 'megabytes available on', driveLetter, 'drive.'); +console.info('Everything is going according to plan.'); +console.info(driveLetter, 'drive has been formatted successfully!');"; + + // Act + IJsEngine jsEngine = null; + JsRuntimeException exception = null; + + try + { + jsEngine = CreateJsEngine(consoleCallback: logger.Log); + jsEngine.Execute(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + finally + { + jsEngine?.Dispose(); + logger.Dispose(); + sb.Clear(); + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Method info not found in YantraJS.Core.Debug.JSConsole", exception.Description); + } + + [Fact] + public void SupportsConsoleWarnMethod() + { + // Arrange + var sb = new StringBuilder(); + var logger = new StringLogger(sb); + + const string input = @"console.warn('Watch out, the doors are closing!'); +console.warn('Watch yourself,', 'be careful!'); +console.warn('It is forbidden to watch!');"; + string targetOutput = "warn: Watch out, the doors are closing!" + Environment.NewLine + + "warn: Watch yourself, be careful!" + Environment.NewLine + + "warn: It is forbidden to watch!" + Environment.NewLine + ; + + // Act + using (var jsEngine = CreateJsEngine(consoleCallback: logger.Log)) + { + jsEngine.Execute(input); + } + + string output = sb.ToString(); + + logger.Dispose(); + sb.Clear(); + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public void SupportsConsoleErrorMethod() + { + // Arrange + var sb = new StringBuilder(); + var logger = new StringLogger(sb); + + const string input = @"console.error('A terrible thing happened!');"; + string targetOutput = "error: A terrible thing happened!" + Environment.NewLine; + + // Act + using (var jsEngine = CreateJsEngine(consoleCallback: logger.Log)) + { + jsEngine.Execute(input); + } + + string output = sb.ToString(); + + logger.Dispose(); + sb.Clear(); + + // Assert + Assert.Equal(targetOutput, output); + } + + private sealed class StringLogger : IDisposable + { + private StringBuilder _buffer; + + + public StringLogger(StringBuilder buffer) + { + _buffer = buffer; + } + + + public void Log(string type, object[] args) + { + if (type != "log") + { + _buffer.AppendFormat("{0}: ", type); + } + + for (int argIndex = 0; argIndex < args.Length; argIndex++) + { + if (argIndex > 0) + { + _buffer.Append(" "); + } + + object arg = args[argIndex] ?? "null"; + var formattableArg = arg as IFormattable; + + if (formattableArg != null) + { + if (formattableArg is DateTime) + { + var dateTime = (DateTime)formattableArg; + DateTime universalDateTime = dateTime.ToUniversalTime(); + + _buffer.Append(universalDateTime.ToString("O", CultureInfo.InvariantCulture)); + } + else + { + _buffer.Append(formattableArg.ToString("G", CultureInfo.InvariantCulture)); + } + } + else + { + _buffer.Append(arg.ToString()); + } + } + + _buffer.AppendLine(); + } + + #region IDisposable implementation + + public void Dispose() + { + _buffer = null; + } + + #endregion + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Yantra/Es2015Tests.cs b/test/JavaScriptEngineSwitcher.Tests/Yantra/Es2015Tests.cs new file mode 100644 index 00000000..9b70b4da --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Yantra/Es2015Tests.cs @@ -0,0 +1,21 @@ +using Xunit; + +namespace JavaScriptEngineSwitcher.Tests.Yantra +{ + public class Es2015Tests : Es2015TestsBase + { + protected override string EngineName + { + get { return "YantraJsEngine"; } + } + + + #region Promises + + [Fact] + public override void SupportsPromises() + { } + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Yantra/Es5Tests.cs b/test/JavaScriptEngineSwitcher.Tests/Yantra/Es5Tests.cs new file mode 100644 index 00000000..da0fea34 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Yantra/Es5Tests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Yantra +{ + public class Es5Tests : Es5TestsBase + { + protected override string EngineName + { + get { return "YantraJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Yantra/EvalTests.cs b/test/JavaScriptEngineSwitcher.Tests/Yantra/EvalTests.cs new file mode 100644 index 00000000..3a34bbe0 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Yantra/EvalTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Yantra +{ + public class EvalTests : EvalTestsBase + { + protected override string EngineName + { + get { return "YantraJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Yantra/InteropTests.cs b/test/JavaScriptEngineSwitcher.Tests/Yantra/InteropTests.cs new file mode 100644 index 00000000..7a2ad049 --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Yantra/InteropTests.cs @@ -0,0 +1,534 @@ +using System; +using System.IO; + +using Xunit; + +using JavaScriptEngineSwitcher.Core; + +using JavaScriptEngineSwitcher.Tests.Interop; +using JavaScriptEngineSwitcher.Tests.Interop.Animals; +using JavaScriptEngineSwitcher.Tests.Interop.Logging; + +namespace JavaScriptEngineSwitcher.Tests.Yantra +{ + public class InteropTests : InteropTestsBase + { + protected override string EngineName + { + get { return "YantraJsEngine"; } + } + + + #region Embedding of objects + + #region Objects with fields + + [Fact] + public override void EmbeddingOfInstanceOfCustomValueTypeWithFields() + { + // Arrange + var date = new Date(2015, 12, 29); + + const string input1 = "date.Year"; + const int targetOutput1 = 2015; + + const string input2 = "date.Month"; + const int targetOutput2 = 12; + + const string input3 = "date.Day"; + const int targetOutput3 = 29; + + // Act + int output1; + int output2; + int output3; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("date", date); + + output1 = jsEngine.Evaluate(input1); + output2 = jsEngine.Evaluate(input2); + output3 = jsEngine.Evaluate(input3); + } + + // Assert + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + Assert.Equal(targetOutput3, output3); + } + + [Fact] + public override void EmbeddingOfInstanceOfCustomReferenceTypeWithFields() + { + // Arrange + var product = new Product + { + Name = "Red T-shirt", + Description = string.Empty, + Price = 995.00 + }; + + const string updateCode = @"product.Description = ''; +product.Price *= 1.15;"; + + const string input1 = "product.Name"; + const string targetOutput1 = "Red T-shirt"; + + const string input2 = "product.Price"; + const double targetOutput2 = 1144.25; + + // Act + string output1; + double output2; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("product", product); + jsEngine.Execute(updateCode); + + output1 = jsEngine.Evaluate(input1); + output2 = jsEngine.Evaluate(input2); + } + + // Assert + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + } + + #endregion + + #region Objects with properties + + [Fact] + public override void EmbeddingOfInstanceOfBuiltinValueTypeWithProperties() + { } + + [Fact] + public override void EmbeddingOfInstanceOfCustomValueTypeWithProperties() + { } + + [Fact] + public override void EmbeddingOfInstanceOfCustomReferenceTypeWithProperties() + { + // Arrange + var person = new Person("Vanya", "Ivanov"); + const string updateCode = @"person.LastName = person.LastName.substr(0, 5) + 'ff'; +person.Patronymic = '';"; + + const string input1 = "person.FirstName"; + const string targetOutput1 = "Vanya"; + + const string input2 = "person.LastName"; + const string targetOutput2 = "Ivanoff"; + + // Act + string output1; + string output2; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("person", person); + jsEngine.Execute(updateCode); + + output1 = jsEngine.Evaluate(input1); + output2 = jsEngine.Evaluate(input2); + } + + // Assert + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + } + + #endregion + + #region Objects with methods + + [Fact] + public override void EmbeddingOfInstanceOfCustomValueTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + var date = new Date(); + + const string input = "date.GetType();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.EmbedHostObject("date", date); + jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Method GetType not found in JavaScriptEngineSwitcher.Tests.Interop.Date", exception.Description); + } + + [Fact] + public override void EmbeddingOfInstanceOfCustomReferenceTypeAndCallingOfItsGetTypeMethod() + { + // Arrange + var cat = new Cat(); + + const string input = @"cat.GetType();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + jsEngine.EmbedHostObject("cat", cat); + jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.Equal("Method GetType not found in JavaScriptEngineSwitcher.Tests.Interop.Animals.Cat", exception.Description); + } + + #endregion + + #region Recursive calls + + #region Mapping of errors + + [Fact] + public void MappingCompilationErrorDuringRecursiveEvaluationOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-evaluation/compilation-error"; + const string input = "evaluateFile('index').calculateResult();"; + const double targetOutput = 132; + + // Act + double output; + + using (var jsEngine = CreateJsEngine()) + { + Func evaluateFile = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("evaluateFile", evaluateFile); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public void MappingRuntimeErrorDuringRecursiveEvaluationOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-evaluation/runtime-error"; + const string input = "evaluateFile('index').calculateResult();"; + const double targetOutput = double.NaN; + + // Act + double output; + + using (var jsEngine = CreateJsEngine()) + { + Func evaluateFile = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("evaluateFile", evaluateFile); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public void MappingHostErrorDuringRecursiveEvaluationOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-evaluation/host-error"; + const string input = "evaluateFile('index').calculateResult();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Func evaluateFile = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("evaluateFile", evaluateFile); + double output = jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.StartsWith("System.IO.FileNotFoundException: Could not find file ", exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Equal("index.js", exception.DocumentName); + Assert.Equal(6, exception.LineNumber); + Assert.Equal(2, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at calculateResult (index.js:6:2)" + Environment.NewLine + + " at Global code (Script Document:1:1)", + exception.CallStack + ); + } + + [Fact] + public void MappingCompilationErrorDuringRecursiveExecutionOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-execution/compilation-error"; + const string variableName = "num"; + + // Act + JsCompilationException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Action executeFile = path => jsEngine.ExecuteFile(path); + + jsEngine.SetVariableValue("directoryPath", directoryPath); + jsEngine.EmbedHostObject("executeFile", executeFile); + jsEngine.ExecuteFile(Path.Combine(directoryPath, "main-file.js")); + + int output = jsEngine.GetVariableValue(variableName); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Compilation error", exception.Category); + Assert.Equal("Unexpected token Hash: #", exception.Description); + Assert.Equal("SyntaxError", exception.Type); + Assert.Equal("second-file.js", exception.DocumentName); + Assert.Equal(1, exception.LineNumber); + Assert.Equal(6, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + } + + [Fact] + public void MappingRuntimeErrorDuringRecursiveExecutionOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-execution/runtime-error"; + const string variableName = "num"; + const int targetOutput = 15; + + // Act + int output; + + using (var jsEngine = CreateJsEngine()) + { + Action executeFile = path => jsEngine.ExecuteFile(path); + + jsEngine.SetVariableValue("directoryPath", directoryPath); + jsEngine.EmbedHostObject("executeFile", executeFile); + jsEngine.ExecuteFile(Path.Combine(directoryPath, "main-file.js")); + + output = jsEngine.GetVariableValue(variableName); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + [Fact] + public void MappingHostErrorDuringRecursiveExecutionOfFiles() + { + // Arrange + const string directoryPath = "Files/recursive-execution/host-error"; + const string variableName = "num"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Action executeFile = path => jsEngine.ExecuteFile(path); + + jsEngine.SetVariableValue("directoryPath", directoryPath); + jsEngine.EmbedHostObject("executeFile", executeFile); + jsEngine.ExecuteFile(Path.Combine(directoryPath, "main-file.js")); + + int output = jsEngine.GetVariableValue(variableName); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.Equal("Runtime error", exception.Category); + Assert.StartsWith("System.IO.FileNotFoundException: File ", exception.Description); + Assert.Equal("Error", exception.Type); + Assert.Equal("first-file.js", exception.DocumentName); + Assert.Equal(2, exception.LineNumber); + Assert.Equal(0, exception.ColumnNumber); + Assert.Empty(exception.SourceFragment); + Assert.Equal( + " at Global code (first-file.js:2)" + Environment.NewLine + + " at Global code (main-file.js:2)", + exception.CallStack + ); + } + + #endregion + + #endregion + + #endregion + + + #region Embedding of types + + #region Creating of instances + + [Fact] + public override void CreatingAnInstanceOfEmbeddedBuiltinValueType() + { } + + [Fact] + public override void CreatingAnInstanceOfEmbeddedCustomExceptionAndCallingOfItsGetTypeMethod() + { + // Arrange + Type loginFailedExceptionType = typeof(LoginFailedException); + + const string input = "new LoginFailedError(\"Wrong password entered!\").GetType();"; + string targetOutput = "function LoginFailedException() { [clr-native] }"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("LoginFailedError", loginFailedExceptionType); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.Equal(targetOutput, output); + } + + #endregion + + #region Types with fields + + [Fact] + public override void EmbeddingOfCustomReferenceTypeWithField() + { + // Arrange + Type defaultLoggerType = typeof(DefaultLogger); + + const string input = "DefaultLogger.Current.ToString()"; + const string targetOutput = "[null logger]"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("DefaultLogger", defaultLoggerType); + + lock (DefaultLogger.SyncRoot) + { + output = jsEngine.Evaluate(input); + } + } + + // Assert + Assert.Equal(targetOutput, output); + } + + #endregion + + #region Types with methods + + [Fact] + public override void EmbeddingOfBuiltinReferenceTypeWithMethods() + { + // Arrange + Type mathType = typeof(Math); + + const string input1 = "Math2.Max(5.37, 5.56)"; + const double targetOutput1 = 5; + + const string input2 = "Math2.Log10(23)"; + const double targetOutput2 = 1.36172783601759; + + // Act + double output1; + double output2; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("Math2", mathType); + output1 = jsEngine.Evaluate(input1); + output2 = Math.Round(jsEngine.Evaluate(input2), 14); + } + + // Assert + Assert.Equal(targetOutput1, output1); + Assert.Equal(targetOutput2, output2); + } + + [Fact] + public override void EmbeddingOfTypeAndCallingOfItsGetTypeMethod() + { } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Yantra/MultithreadingTests.cs b/test/JavaScriptEngineSwitcher.Tests/Yantra/MultithreadingTests.cs new file mode 100644 index 00000000..3cbf2a6b --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Yantra/MultithreadingTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Yantra +{ + public class MultithreadingTests : MultithreadingTestsBase + { + protected override string EngineName + { + get { return "YantraJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/Yantra/PrecompilationTests.cs b/test/JavaScriptEngineSwitcher.Tests/Yantra/PrecompilationTests.cs new file mode 100644 index 00000000..86713d2e --- /dev/null +++ b/test/JavaScriptEngineSwitcher.Tests/Yantra/PrecompilationTests.cs @@ -0,0 +1,10 @@ +namespace JavaScriptEngineSwitcher.Tests.Yantra +{ + public class PrecompilationTests : PrecompilationTestsBase + { + protected override string EngineName + { + get { return "YantraJsEngine"; } + } + } +} \ No newline at end of file diff --git a/test/JavaScriptEngineSwitcher.Tests/packages.config b/test/JavaScriptEngineSwitcher.Tests/packages.config deleted file mode 100644 index ef8b4aca..00000000 --- a/test/JavaScriptEngineSwitcher.Tests/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file